home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1997 February: Technology Seed / Mac Tech Seed Feb '97.toast / OpenDoc 1.2b2c1 / Implementation / Storage / Bento / CM / TOCIO.c < prev    next >
Encoding:
Text File  |  1997-02-13  |  115.6 KB  |  2,411 lines  |  [TEXT/MPS ]

  1. /*
  2.     File:        TOCIO.c
  3.  
  4.     Contains:    Container Manager TOC I/O Routines
  5.  
  6.     Written by:    Ira L. Ruben
  7.  
  8.     Owned by:    Ed Lai
  9.  
  10.     Copyright:    © 1992 - 1996 by Apple Computer, Inc., all rights reserved.
  11.  
  12.     Change History (most recent first):
  13.  
  14.          <3>     8/13/96    DM        1362809: disable containers on error
  15.          <2>     1/15/96    TJ        Cleaned Up
  16.          <8>     7/28/95    EL        #1272079: Extra precaution to make sure
  17.                                     continue flag is set correctly.
  18.          <7>     4/25/95    EL        1242376: Separate crash proof for embedded
  19.                                                     and top level container.
  20.          <6>     3/24/95    EL        1226127: if CMCRASH_PROOF do not overwrite
  21.                                                     old TOC before new one is written.
  22.          <5>     2/27/95    EL        1222909: When cmEnterGlobalName fails,
  23.                                                     memory is release twice.
  24.          <4>     12/9/94    EL        #1182275 Set continue flag before writing
  25.                                                     to TOC fix is now permanent.
  26.          <3>     9/22/94    EL        #1182275 Continue flag not set correctly
  27.                                                     somewhere, so set the flag whenever there
  28.                                                     is a next value.
  29.          <2>     8/26/94    EL        #1182275 allows truncation. #1182308 use
  30.                                                     CMFormatData with negative size instead of
  31.                                                     memcpy.
  32.          <7>      4/8/94    EL        Get around longPtr++ bug in MetroWerk.
  33.          <6>     3/31/94    EL        Use refcon to indicate if glboal name is on
  34.                                                     disk. #1150214
  35.          <5>     3/17/94    EL        Make sure that immediate value < 4 bytes
  36.                                                     are aligned properly regardless of endian-
  37.                                                     ness. #1149979
  38.          <4>      2/3/94    EL        Bento File names are now eight chars or
  39.                                                     less.
  40.          <3>    11/23/93    EL        When updating save TOC data so that TOC
  41.                                                     don't need to be read twice.
  42.          <2>    10/21/93    EL        In cmReadTOC, allow restriction of kind of
  43.                                                      entry read.
  44.          <2>    10/21/93    EL        In cmReadTOC, allow restriction of kind of
  45.                                                     entry read.
  46.  
  47.     To Do:
  48. */
  49.  
  50. /*---------------------------------------------------------------------------*
  51.  |                                                                           |
  52.  |                              <<< TOCIO.c >>>                              |
  53.  |                                                                           |
  54.  |                     Container Manager TOC I/O Routines                    |
  55.  |                                                                           |
  56.  |                               Ira L. Ruben                                |
  57.  |                                 10/01/92                                  |
  58.  |                                                                           |
  59.  |                     Copyright Apple Computer, Inc. 1992-1995              |
  60.  |                           All rights reserved.                            |
  61.  |                                                                           |
  62.  *---------------------------------------------------------------------------*
  63.  
  64.  This file contains all the routines responsible for reading and writing the container
  65.  TOC.  The routines are divided into two main groups as follows:
  66.  
  67.  (1). "Raw" low-level TOC I/O: all the routines to read and write TOC basic value segment
  68.            entries.  These routines format the TOC with the syntatic layout described in the
  69.           header (.h) file.
  70.  
  71.            cmStartTOCIO                        -    initiate TOC I/O
  72.             cmEndTOCIO                            - end TOC I/O
  73.             cmFtellTOC                            - get current container TOC I/O "seek" position
  74.             cmFlushTOCOutputBuffer    - write current TOC output buffer
  75.             cmWrite1TOCSegment            - write one value segment
  76.                 writeTOCEntry                    - write one TOC control code and its data
  77.             cmRead1TOCSegment                - read one value segment
  78.                  readTOCEntry                    - read one TOC control code and its data
  79.                 
  80.  (2). High-level TOC I/O control that determines how to generate or process TOC entries.
  81.             This calles the group (1) routines.
  82.             
  83.             cmBuildGlobalNameTable    - add a global name to the global name table
  84.             cmReadTOC                                - read (laod) in a container TOC
  85.             cmWriteTOC                            - write a TOC to the container
  86.                 writeTOCValue                    - add value data info to segment and write the segment
  87.                 writeTOCValueHdr            - add value header info to segment
  88.                 writeTOCProperty            - add property info to segment
  89.                 writeTOCObject                - add object info to segment
  90.             cmDoBackPatches                    - back patch certain TOC entries
  91. */
  92.  
  93. #include <stddef.h>
  94. #include <string.h>
  95. #include <setjmp.h>
  96. #include <stdio.h>
  97.  
  98. #ifndef __CMTYPES__
  99. #include "CMTypes.h"
  100. #endif
  101. #ifndef __CM_API__
  102. #include "CMAPI.h"
  103. #endif
  104. #ifndef __LISTMGR__
  105. #include "ListMgr.h"
  106. #endif
  107. #ifndef __TOCENTRIES__
  108. #include "TOCEnts.h"   
  109. #endif
  110. #ifndef __TOCOBJECTS__
  111. #include "TOCObjs.h"   
  112. #endif
  113. #ifndef __TOCIO__
  114. #include "TOCIO.h"
  115. #endif
  116. #ifndef __GLOBALNAMES__
  117. #include "GlbNames.h"   
  118. #endif
  119. #ifndef __CONTAINEROPS__
  120. #include "Containr.h"  
  121. #endif
  122. #ifndef __HANDLERS__
  123. #include "Handlers.h"
  124. #endif
  125. #ifndef __UPDATING__
  126. #include "Update.h"  
  127. #endif
  128. #ifndef __DYNAMICVALUES__
  129. #include "DynValus.h"     
  130. #endif
  131. #ifndef __BUFFEREDIO__
  132. #include "BufferIO.h"  
  133. #endif
  134. #ifndef __FREESPACE__
  135. #include "FreeSpce.h" 
  136. #endif
  137. #ifndef __REFERENCES__
  138. #include "Refs.h"      
  139. #endif
  140. #ifndef __SESSIONDATA__
  141. #include "Session.h"          
  142. #endif
  143. #ifndef __ERRORRPT__
  144. #include "ErrorRpt.h"      
  145. #endif
  146. #ifndef __UTILITYROUTINES__
  147. #include "Utility.h"        
  148. #endif
  149.  
  150.                                                                     CM_CFUNCTIONS
  151.  
  152. /* The following generates a segment directive for Mac only due to 32K Jump Table             */
  153. /* Limitations.  If you don't know what I'm talking about don't worry about it.  The        */
  154. /* default is not to generate the pragma.  Theoritically unknown pragmas in ANSI won't    */
  155. /* choke compilers that don't recognize them.  Besides why would they be looked at if        */
  156. /* it's being conditionally skipped over anyway?  I know, famous last words!                        */
  157.  
  158. #if CM_MPW
  159. #pragma segment TOCEntries
  160. #endif
  161.  
  162.  
  163.                                                             /*-----------------------*
  164.                                                              | TOC I/O Control Block |
  165.                                                              *-----------------------*/
  166.  
  167. /* This is the TOC I/O control block which is passed around to the various TOC I/O             */
  168. /* routines.  It contains the buffers, associated pointers, etc.  For callers outside        */
  169. /* this file, they con only "see" this block through an anonymous "void *" pointer.            */
  170.  
  171. struct TOCIOControl {                                            /* Layout of a TOC I/O control block:                    */
  172.     ContainerPtr         container;                            /*        container control block ptr                            */
  173.     CM_ULONG                tocBufSize;                            /*        max size of the TOC I/O buffer                    */
  174.     union {                                                                    /*        setjmp environment for buffered I/O            */
  175.         jmp_buf                jmpBuf;                                    /*              *** PORTABILITY PROBLEM ***                */
  176.         CM_CHAR                firstByte;                            /*       Don't assume the layout of a jmp_buf    */
  177.     } tocIOEnv;                                                            /*             firstByte used to memcpy to  jmp_buf    */
  178.     CMBoolean                reading;                                /*        true ==> reading TOC; false ==> writing    */
  179.     TOCCodeArray        entrySize;                            /*        entry size corresponding to each code        */
  180.     CM_ULONG                totalRead;                            /*        total number of bytes read so far                */
  181.     CM_ULONG                 ioPos;                                    /*        current container I/O position                    */
  182.     CM_ULONG                currBufrPos;                        /*        current buffer container position                */
  183.     CM_ULONG                currBufSize;                        /*        buffer size of current read buffer            */
  184.     CM_ULONG                tocSize;                                /*        total (current write) size of the TOC        */
  185.     CM_ULONG                 nextByte;                                /*        0-rel. next byte to use in tocBuffer        */
  186.     TOCentry               prevSegment;                        /*        previous TOC segment entry                             */
  187.     #if CMDUMPTOC
  188.     CM_ULONG                currTOCoffset;                    /*        offset to most recent TOC entry                    */
  189.     #endif
  190.     CM_UCHAR                 tocBuffer[1];                        /*         the START of the TOC I/O buffer                    */
  191. };
  192. typedef struct TOCIOControl TOCIOControl, *TOCIOControlPtr;
  193.  
  194. /* CAUTION: I/O error "recovery" here is done by using a longjmp on a setjmp environment*/
  195. /*                     passed to startTOCIO() below. In order to save that environment, a memcpy() */
  196. /*                    is done to copy the startTOCIO() parameter to the above struct.  No                 */
  197. /*                  assumptions are made about the layout of a setjmp's jmp_buf!  For example,     */
  198. /*                     it is never assumed to be an array.  Therefore, in order to PORTABLY pass     */
  199. /*                    the jmp_buf address to a memcpy(), a union is done to the first byte of the */
  200. /*                    jmp_buf to address it.  To some "picky" compilers addressing the first byte */
  201. /*                     of a jmp_buf which IS*/
  202. /*                    an array as "&foo" is treated as an error (it expects "&foo[0]" -- I always */
  203. /*                    thought this was benign).  To avoid such problems, the above "kludge" is         */
  204. /*                    used.                                                                                                                                             */
  205.  
  206.  
  207.                                                      /*--------------------------------*
  208.                                                         | TOC Writing Communication Area |
  209.                                                         *--------------------------------*/
  210.  
  211. /* The following is used in this file by cmWriteTOC(), etc. to common info needed to be    */
  212. /* maintained while walking the TOC as it is being written.  The pointer to the callers    */
  213. /* back patch records is also in here so that those records may eventually be passed to    */
  214. /* cmDoBackPatches().                                                                                                                                        */
  215.  
  216. struct WriteTOCCommArea {                /* cmWriteTOC() TOC walking communication area layout:    */
  217.     TOCentry              tocEntry;            /*        the TOC entry buffer to be created and written        */
  218.     TOCObjectPtr      refDataObject;    /*         NULL or ptr to references recording object                */
  219.     BackPatchesPtr thePatches;        /*        ptr to the TOC entries saved for back-patching        */
  220. };
  221. typedef struct WriteTOCCommArea WriteTOCCommArea, *WriteTOCCommAreaPtr;
  222.  
  223.  
  224. /*-------------------------------------------------------*
  225.  | cmStartTOCIO - prepare for reading or writing the TOC |
  226.  *-------------------------------------------------------*
  227.  
  228.  This must be called prior to any reading or writing of the TOC.  The basic standard
  229.  protocol to be followed is as follows:
  230.  
  231.      if (setjmp(env)) {
  232.         <caller's recovery and exit>
  233.     }
  234.     
  235.     CMseek(to proper TOC position in the container);
  236.     
  237.     t = cmStartTOCIO(container, container->tocBufSize, (jmp_buf *)&env, tocOffset, tocSize);
  238.     
  239.                          when reading                  or          when writing
  240.                          ------------                              ------------
  241.                         
  242.             while (cmRead1TOCSegment(t, ...)) {   |   some kind loop through the TOC
  243.                 cmRead1TOCSegment(t, ...);          |   {
  244.                 <process TOC entry>                 |             <set up TOC entry>
  245.             }                                     |      cmWrite1TOCSegment(t, ...);
  246.                                                   |   }
  247.                                                                                         |   cmFlushTOCOutputBuffer(t, true);
  248.     cmEndTOCIO(t);
  249.  
  250.  The TOC to be read or written is assumed to start at the specified tocOffset. It is the
  251.  caller's responsibility to seek to this position.  For reading a TOC, tocSize defines the
  252.  EXACT amount of TOC to be read.  For writing, tocSize must be passed as 0. The final size
  253.  will be returned when endTOCIO() is called to terminate TOC creation (alternatively,
  254.  cmFlushTOCOutputBuffer() will return the final size).
  255.   
  256.  cmStartTOCIO() allocates an I/O buffer and its associated control information. The buffer
  257.  is used to format or extract TOC entries.  The TOC I/O control block pointer allocated is
  258.  returned as the function result as an anonymous "void *" pointer.  This is passed to all
  259.  the other low level TOC externalized buffering routines (cmWrite1TOCSegment(),
  260.  cmread1TOCSegment(), cmFlushTOCOutputBuffer(), cmFTellTOC(), and endTOCIO()).  An error
  261.  is reported for allocation errors.  If the error reporter returns, NULL is returned as
  262.  the function result.
  263.  
  264.  The maximum size of the buffer to be allocated is passed.  This will generally be
  265.  determined from the container label, but that is not assumed here.  The size is, however,
  266.  always ensured to be rounded up to a multiple of 4.
  267.  
  268.  A set setjmp/longjmp environment variable is also passed for read and write error
  269.  reporting and recovery.  The setjmp/longjmp is used rather than have to check for errors
  270.  on each I/O call of the buffered I/O routines.  If the longjmp is taken, the caller can
  271.  assume that the appropriate error has been reported and the buffer and its control block
  272.  freed.
  273.   
  274.  Note, in the case of updating, the private TOC and the non-private (updating) TOC are
  275.  viewed as two INDEPENDENT TOCs.  There is no need to worry that the non-private (i.e.,
  276.  second TOC) does not start on a buffer boundary with respect to the private (first) TOC.
  277.  Each is read or written independently and viewed as distinct from one another.  They just
  278.  happen to be adjacent to one another in the container.  The tocBufSize and tocOffset
  279.  must be appropriately passed for each TOC.
  280.  
  281.  Also note, that the reason some of the low level buffering routines have been made
  282.  external (cmWrite1TOCSegment(), cmread1TOCSegment(), cmFlushTOCOutputBuffer(), 
  283.  cmFTellTOC(), and endTOCIO()) is to mainly allow for the debugging trace routines to read
  284.  the TOC.  cmWrite1TOCSegment() and cmFlushTOCOutputBuffer() are external only for
  285.  symmetry, but it should have no external callers (i.e., they're all in this file).  
  286.  Making these routines externally visable is also why the I/O control block pointer is
  287.  returned as an anonymous "void *".
  288. */
  289.  
  290. void *cmStartTOCIO(ContainerPtr container, CM_ULONG tocBufSize, jmp_buf *ioEnv,
  291.                                      CM_ULONG tocOffset, CM_ULONG tocSize)
  292. {
  293.     TOCIOControlPtr t;
  294.     
  295.     /* Allocate the control block with the buffer as the last field of the block...                */
  296.     
  297.     tocBufSize = 4 * ((tocBufSize + 3) / 4);                                /* ensure a multiple of 4            */
  298.     
  299.     if ((t = (TOCIOControlPtr)CMmalloc(sizeof(TOCIOControl) + tocBufSize)) == NULL) {
  300.         Container_Disable(container);
  301.         ERROR1(CM_err_NoTOCBuffer, CONTAINERNAME);
  302.         return (NULL);
  303.     }
  304.  
  305.     t->container         = container;                                                        /* remember the container            */
  306.     t->tocBufSize         = tocBufSize;                                                    /* remember buffer size                */
  307.     memcpy(&t->tocIOEnv.firstByte, ioEnv, sizeof(jmp_buf)); /* remember setjmp env.                */
  308.     
  309.     InitCodeSizes(t->entrySize);                                                        /* init TOC entry size array    */
  310.     
  311.     t->nextByte       = 0;                                                                            /* next read/write byte    ptr        */
  312.     t->ioPos               = tocOffset;                                                            /* starting container offset    */
  313.     t->currBufrPos = tocOffset;                                                            /* 1st buffer always the same    */
  314.     t->tocSize           = tocSize;                                                                /* total amount (0 for write)    */
  315.     t->currBufSize = 0;                                                                            /* 1st read buffer is empty        */
  316.     t->totalRead     = 0;                                                                            /* because nothing read yet        */
  317.     
  318.     t->prevSegment.objectID        = 0x00000000UL;                                /* init to no previous object    */
  319.     t->prevSegment.propertyID    = 0x00000000UL;                                /* init to no previous prop.    */
  320.     t->prevSegment.typeID            = 0x00000000UL;                                /* init to no previous type        */
  321.     t->prevSegment.generation    = 0;                                                    /* init to no previous gen.        */
  322.     
  323.     return ((void *)t);                                                                            /* pass back ctl block ptr        */
  324. }
  325.  
  326.  
  327. /*---------------------------------------------------*
  328.  | cmEndTOCIO - terminate reading or writing the TOC |
  329.  *---------------------------------------------------*
  330.  
  331.  After all I/O to a TOC is completed, this routine must be called to free the TOC I/O
  332.  buffer and control block (whose pointer is passed as the t parameter).  If the TOC was
  333.  created (written), then it is assumed the last buffer was already flushed by calling
  334.  flushTOCOutputBuffer() with its finalFlush parameter set to true. 
  335.  
  336.  The function returns the size of the TOC.  For TOC creation (writing), this will be the
  337.  size of the TOC now written to the container.  For reading, the returned value is 
  338.  identical to the size passed to cmStartTOCIO() (i.e., the returned value is not looked at
  339.  for reading).
  340.  
  341.  Note, this routine must NOT be called if an error was reported and the longjmp taken 
  342.  through the setjmp environment buffer passed to cmStartTOCIO()>  Part of standard error
  343.  recovery for TOC I/O errors is effectively do a cmEndTOCIO() and then report the error.
  344.  If the error reporter returns, the longjmp is taken.
  345. */
  346.  
  347. CM_ULONG cmEndTOCIO(void *tocIOCtl)
  348. {
  349.     TOCIOControlPtr t = (TOCIOControlPtr)tocIOCtl;    /* convert the anonymous I/O block ptr*/
  350.     ContainerPtr         container = t->container;                /* needed for CMfree()                                */
  351.     CM_ULONG                 tocSize = t->tocSize;
  352.                          
  353.     CMfree(t);                                                                            /* free buffer and control block            */
  354.     
  355.     return (tocSize);                                                                /* return total size of the TOC                */
  356. }
  357.  
  358.  
  359. /*--------------------------------------------------------*
  360.  | cmFtellTOC - return current TOC container I/O position |
  361.  *--------------------------------------------------------*
  362.  
  363.  This returns the current container I/O position.  The position is updated as the TOC I/O
  364.  buffer fills and is written or reloaded when reading.
  365.  
  366.  This routine is needed when only when there is the possibility that other code might be
  367.  doing "seeks" to the same container behind the TOC I/O routine's back!  Code doing such
  368.  seeks must be able to reseek the container position according to the value returned here.
  369. */
  370.  
  371. CM_ULONG cmFtellTOC(void *tocIOCtl)
  372. {
  373.     return (((TOCIOControlPtr)tocIOCtl)->ioPos);
  374. }
  375.  
  376.  
  377. /*------------------------------------------------------*
  378.  | cmFlushTOCOutputBuffer - flush the TOC output buffer |
  379.  *------------------------------------------------------*
  380.  
  381.  This must be called to make sure that the TOC output buffer (allocated by cmStartTOCIO()
  382.  and associated with tocIOCtl) is flushed, i.e., fully written.  This buffer will be the
  383.  remaining partial buffer when finalFlush is true, and a full buffer otherwise.  The 
  384.  buffer is padded if necessary up to the full buffer size (defined by cmStartTOCIO()) or
  385.  to the nearest multiple of 4 when finalFlush is true.  finalFlush would, of course, only
  386.  be true prior to calling cmEndTOCIO() to write the LAST buffer out as a "short" buffer.
  387.  
  388.  Padding consists of a EndOfBufr byte followed by 0 or more NOP bytes.  Padding is not
  389.  necessary, of course, if the buffer is exactly full.
  390.  
  391.  The function returns the current total size of the TOC including the flushed buffer. This
  392.  generally has no meaning until finalFlush is true, at which time the returned amount will
  393.  be the total size of the TOC written to the container. 
  394.  
  395. */
  396.  
  397. CM_ULONG cmFlushTOCOutputBuffer(void *tocIOCtl, CMBoolean finalFlush)
  398. {
  399.     TOCIOControlPtr t = (TOCIOControlPtr)tocIOCtl;
  400.     ContainerPtr         container = t->container;
  401.     CM_ULONG                 bufSize, padAmount;
  402.     CM_UCHAR                 *p, entryCode;
  403.     jmp_buf                 jmpBuf;
  404.     
  405.     /* Determine the buffer size to use: a full buffer if finalFlush is false, and the         */
  406.     /* next multiple of 4 if this is the last buffer.                                                                            */
  407.     
  408.     bufSize = finalFlush ? 4 * ((t->nextByte + 3) / 4) : t->tocBufSize;
  409.     
  410.     /* Pad the remaining bytes in the buffer (if any) with a EndOfBufr and 0 or more NOPs.*/
  411.     
  412.     padAmount = bufSize - t->nextByte;                                    /* number of pad bytes needed            */
  413.     
  414.     if (padAmount > 0) {                                                                /* if padding needed...                        */
  415.         p = t->tocBuffer + t->nextByte;                                        /* ...point at where to start pad    */
  416.         entryCode = EndOfBufr;                                                        /* ...put in the EndOfBufr code        */
  417.         CMformatData(container, p, 1, &entryCode);
  418.         if (padAmount > 1) {                                                            /* ...if more pads still needed...*/
  419.             entryCode = NOP;                                                                /* ...pad out with the NOPs                */
  420.             for (--padAmount; padAmount > 0; --padAmount)
  421.                 CMformatData(container, ++p, 1, &entryCode);
  422.         }
  423.     } /* padding */
  424.     
  425.     /* Write the buffer to the container...                                                                                                */
  426.     
  427.     if (CMfwrite(container, t->tocBuffer, sizeof(CM_UCHAR), bufSize) != (CMSize)bufSize) {
  428.         memcpy(&jmpBuf, &t->tocIOEnv.firstByte, sizeof(jmp_buf)); 
  429.         Container_Disable(container);
  430.         CMfree(t);
  431.         ERROR1(CM_err_BadTOCWrite, CONTAINERNAME);
  432.         longjmp(jmpBuf, 1);
  433.     }
  434.     
  435.     t->ioPos      += bufSize;                                                            /* update the container position    */
  436.     t->tocSize += bufSize;                                                            /* total the TOC size                            */
  437.     
  438.     t->currBufrPos = t->ioPos;                                                    /* remember offset to this buffer    */
  439.     t->nextByte       = 0;                                                                    /* reset buffer byte ptr                     */
  440.     
  441.     t->prevSegment.generation = 0;                                            /* force gen for next OPT | PT | T*/
  442.  
  443.     return (t->tocSize);                                                                /* return current total TOC size    */
  444. }
  445.  
  446.  
  447. /*------------------------------------------*
  448.  | writeTOCEntry - write a single TOC entry |
  449.  *------------------------------------------*
  450.  
  451.  This is the low-level interface to write one entry to the TOC to the buffer allocated by
  452.  cmStartTOCIO() and associated with the t control block parameter.  The entry's code 
  453.  (entryCode) and a pointer to the entry data (tocEntryData) are passed.  The codes 
  454.  determine the size of the entry (plus one for the code) and hence how much data the
  455.  pointer is pointing to.  The data is always multiples of 4.  Thus the pointer is to an
  456.  array of (unsigned) longs assumed aligned on a boundary appropriate to a long.
  457.  
  458.  The function returns the container offset to the entry data, i.e., where it will be when
  459.  the buffer is written to the container.  This is done because some entries must be back
  460.  patched later.  The value returned from here must therefore be saved for such entries.
  461.  Note, that this is an offset to the data itself, NOT the code placed in front of that
  462.  data, since it is the data that must be back patched and it is the offset of interest.
  463. */
  464.  
  465. static CM_ULONG CM_NEAR writeTOCEntry(TOCIOControlPtr t, CM_UCHAR entryCode,
  466.                                                                             CM_ULONG *tocEntryData)
  467. {
  468.     ContainerPtr         container = t->container;
  469.     CM_ULONG                 nextFree, writePos, entrySize;
  470.     CM_UCHAR                *p;
  471.     
  472.     /* Get the buffer position where the next entry is to be placed and its size                     */
  473.     /* according to the code.                                                                                                                            */
  474.     
  475.     writePos  = t->nextByte;                                            /* where to put next entry in  buffer        */
  476.     entrySize = t->entrySize[entryCode];                    /* entry data size                                            */
  477.     
  478.     /* The entry data plus its code must fit completely in the buffer.  If it doesn't,         */
  479.     /* flush the buffer and put the entry in a "new" (empty) buffer.                                            */
  480.     
  481.     nextFree = writePos + 1 + entrySize;                    /* next free byte after entry                        */
  482.     
  483.     if (nextFree > t->tocBufSize) {                                /* if entry doesn't fit entirely...            */
  484.         (void)cmFlushTOCOutputBuffer(t, false);            /* ...flush the buffer                                    */
  485.         nextFree = 1 + entrySize;                                        /* ...reset next free position                    */
  486.         writePos = 0;                                                                /* ...and the write buffer position            */
  487.     }
  488.  
  489.     t->nextByte = nextFree;                                                /* set next free position in buffer            */
  490.     
  491.     /* Copy the data to the buffer...                                                                                                            */
  492.     
  493.     p = t->tocBuffer + writePos;                                    /* point to where to put it                            */
  494.     
  495.     CMformatData(container, p++, 1, &entryCode);    /* put in the code                                            */
  496.     
  497.     if ((entryCode >= Immediate1) && (entryCode <= ContdImmediate4) && 
  498.                                 (t->prevSegment.typeID != CM_StdObjID_TOC_Type))
  499.         /* immediate data are moved directly unless they belong to object 1                                    */
  500.         CMformatData(container,p, -4,tocEntryData);/* format 4 endian neutral bytes                    */
  501.     else
  502.         while (entrySize > 0) {                                                /* then put in the data itself...            */
  503.             CMformatData(container,p, 4,tocEntryData);/* ...in chunks of 4 bytes                            */
  504.             tocEntryData++;                                                    /* do it here to get around MetroWerk Bug */
  505.             p                 += 4;
  506.             entrySize    -= 4;
  507.         }
  508.     
  509.     /* Return container offset to the data in the buffer (NOT to the code in front)...        */
  510.     
  511.     return (t->currBufrPos + writePos + 1);                /* container offset to the data                    */
  512. }
  513.  
  514.  
  515. /*----------------------------------------*
  516.  | readTOCEntry - read a single TOC entry |
  517.  *----------------------------------------*
  518.  
  519.  This is the low-level interface to read one entry from the TOC from the buffer allocated
  520.  by startTOCIO() and associated with the t control block parameter.  The entry's code is
  521.  returned as the function result and the entry data copied to the long array pointed to by
  522.  tocEntryData.  The codes determine the size of the entry (plus one for the code) and hence
  523.  how much data the pointer will be pointing to.  The data is always multiples of 4.  Thus
  524.  the pointer to the array of (unsigned) longs is assumed aligned on a boundary appropriate
  525.  to a long.  It is assumed there are enough longs to hold all the data (see code
  526.  definitions).
  527.  
  528.  An "eof" condition is detected when the entire TOC has been read.  The entire TOC is
  529.  considered read when the amount of bytes specified to cmStartTOCIO() has been processed.
  530.  The "eof" is signalled by returning the special entry code, TOCEOF.  This code is NOT part
  531.  of the TOC and only returned from here when ALL TOC entries have been read.
  532. */
  533.  
  534. static CM_UCHAR CM_NEAR readTOCEntry(TOCIOControlPtr t, CM_ULONG *tocEntryData)
  535. {
  536.     ContainerPtr         container = t->container;
  537.     CMSize                     bufSize, amountRead;
  538.     CM_UCHAR                 entryCode, *p;
  539.     CM_ULONG                 remaining, entrySize;
  540.     jmp_buf                 jmpBuf;
  541.     CM_CHAR                 errStr[15];
  542.  
  543.     /* Process the next byte, which is assumed to always be a entry code byte. NOPs are     */
  544.     /* ignored,    so we loop back to process the next byte.  EndOfBufr cause the buffer to    */
  545.     /* be reloaded.                                                                                                                                                */
  546.     
  547.     for (;;) {                                                                        /* loop on NOPs and EndOfBufr...                */
  548.     
  549.         /* Reload the input buffer if all the bytes in it have been processed.  currBufSize */
  550.         /* defines the size of the current input buffer.  Return TOCEOF as the entry code     */
  551.         /* if all the bytes of the TOC have been read.                                                                            */
  552.         
  553.         if (t->nextByte >= t->currBufSize) {                /* if all the bytes were processed...        */
  554.             t->currBufrPos = t->ioPos;                                /* ...remember where new buffer begins    */
  555.             
  556.             remaining = t->tocSize - t->totalRead;        /* ...reload the buffer...                            */
  557.             
  558.             if (remaining == 0) {                                            /* if all TOC bytes have been read...        */
  559.                 #if CMDUMPTOC
  560.                 t->currTOCoffset = t->currBufrPos;            /* ...tell debugging world where we are    */
  561.                 #endif
  562.                 return (TOCEOF);                                                /* ...return "eof" condition entry code    */
  563.             }
  564.             
  565.             bufSize      = (remaining > t->tocBufSize) ? (CMSize)t->tocBufSize : (CMSize)remaining;
  566.             amountRead = CMfread(container, t->tocBuffer, sizeof(CM_UCHAR), bufSize);
  567.             
  568.             if (amountRead != bufSize || amountRead == 0) {
  569.                 memcpy(&jmpBuf, &t->tocIOEnv.firstByte, sizeof(jmp_buf)); 
  570.                 Container_Disable(container);
  571.                 CMfree(t);
  572.                 ERROR1(CM_err_BadTOCRead, CONTAINERNAME);
  573.                 longjmp(jmpBuf, 1);
  574.             }
  575.             
  576.             t->currBufSize = bufSize;                                    /* ...set new buffer size                                */
  577.             
  578.             t->totalRead  += bufSize;                                    /* ...total the amount read                            */
  579.             t->ioPos            += bufSize;                                    /* ...remember container position                */
  580.             
  581.             t->nextByte      = 0;                                                /* ...start processing from 1st byte        */
  582.         } /* reloading buffer */
  583.         
  584.         /* Get the entry code first so we know how to process the entry data (if any) that    */
  585.         /* follows...                                                                                                                                                */
  586.         
  587.         #if CMDUMPTOC
  588.         t->currTOCoffset = t->currBufrPos +                    /* tell interested parties the TOC             */
  589.                                              t->nextByte;                            /*   offset we're about to read from        */
  590.         #endif
  591.  
  592.         p = t->tocBuffer + t->nextByte;                            /* point at the code byte in the buffer    */
  593.         
  594.         CMextractData(container, p++,1, &entryCode);/* get code byte                                                */
  595.             
  596.         if (entryCode == EndOfBufr) {                                /* if EndOfBufr...                                            */
  597.             t->nextByte = t->currBufSize;                            /* ...pretend we read all bytes                    */
  598.             continue;                                                                    /* ...loop back to reload the buffer        */
  599.         }
  600.     
  601.         ++t->nextByte;                                                            /* set pointer to next byte after code    */
  602.         
  603.         if (entryCode != NOP) break;                                /* if NOPs, loop to process next byte        */
  604.     } /* for */
  605.     
  606.     /* At this point the entry code has been extracted and it is not one of the special        */
  607.     /* codes (NOP and EndOfBufr).  Verify that the code we do have is valid.  We assume        */
  608.     /* nothing is safe!                                                                                                                                        */
  609.     
  610.     if (entryCode < FirstCode || entryCode > LastCode) {        /* error if not valid range        */
  611.         memcpy(&jmpBuf, &t->tocIOEnv.firstByte, sizeof(jmp_buf)); 
  612.         Container_Disable(container);
  613.         CMfree(t);
  614.         ERROR2(CM_err_BadTOCCode, cmltostr(entryCode, -2, true, errStr), CONTAINERNAME);
  615.         longjmp(jmpBuf, 2);
  616.     }
  617.     
  618.     /* Now we have a valid entry code.  Get the corresponding data size and make sure that*/
  619.     /* all the data can be extracted from the buffer according to that code.  This is         */
  620.     /* another validation check.                                                                                                                    */
  621.     
  622.     entrySize = t->entrySize[entryCode];                        /* get entry data size                                */
  623.     
  624.     t->nextByte += entrySize;                                                /* point where next entry will begin    */
  625.     
  626.     if (t->nextByte > t->currBufSize) {                            /* error if all bytes not in buffer        */
  627.         memcpy(&jmpBuf, &t->tocIOEnv.firstByte, sizeof(jmp_buf)); 
  628.         Container_Disable(container);
  629.         CMfree(t);
  630.         ERROR1(CM_err_TOCoutOfSync, CONTAINERNAME);
  631.         longjmp(jmpBuf, 3);
  632.     }
  633.  
  634.     /* Extract the data from the buffer and it pass back in the array passed by    the                */
  635.     /* caller...                                                                                                                                                    */
  636.     
  637.     if ((entryCode >= Immediate1) && (entryCode <= ContdImmediate4) && 
  638.                                 (t->prevSegment.typeID != CM_StdObjID_TOC_Type))
  639.         /* immediate data are moved directly unless they belong to object 1                                    */
  640.         CMextractData(container, p, -4, tocEntryData);
  641.     else
  642.         while (entrySize > 0) {                                                /* copy groups of 4-byte chunks...        */
  643.             CMextractData(container, p, 4, tocEntryData);
  644.             tocEntryData++;                                                    /* do it here to get around MetroWerk Bug */
  645.             p               += 4;
  646.             entrySize    -= 4;
  647.         }
  648.         
  649.     return (entryCode);                                                            /* entry code as function result            */
  650. }
  651.  
  652.  
  653. /*-------------------------------------------------------*
  654.  | cmWrite1TOCSegment - write a single TOC value segment |
  655.  *-------------------------------------------------------*
  656.   
  657.  All TOC value segment writing is done with this routine.  This routine interfaces the
  658.  low-level TOC entry buffering routines (whose I/O control block is associated with
  659.  tocIOCtl and allocated by cmStartTOCIO()) with the upper-level writers.  The upper level
  660.  (i.e., the caller) views all value segments in terms of the fields shown as this
  661.  routine's parameters.  Here that information is selectively passed to the lower-level
  662.  buffering routines to generate a TOC in the container with the following syntactic layout:
  663.  
  664.                                                  --                        --   
  665.                                                    |  ( PT [g] [R] v... )     |   
  666.                           { OPT [g] [R] v... |  <                 > ... | } ...
  667.                                                    |  ( T  [g] [R] v... )     |   
  668.                                                  --                        --   
  669.                                                     
  670.  where OPT = object entry (NewObject code plus 4-byte object, property, and type ID)
  671.               PT  = property entry (NewProperty code plus 4-byte property and type ID)
  672.              T   = type entry (newType plus 4-byte type ID)
  673.              g   = optional generation number if different from previous generation number
  674.              R     = object ID of value's references recording object (in refDataObject's ID)
  675.              v   = value entry (code plus offset/length or 4-byte immediate) -- code determines
  676.                           4 or 8 byte offsets and actual size of immediate
  677.                          
  678.  In other words, nothing is repeated if it doesn't change. This routine thus remembers and
  679.  controls what goes out and when.  Note that, at a minimum, a data value (v) is always
  680.  generated.
  681.  
  682.  The "R" represents the object ID from the refDataObject parameter.  refDataObject is a
  683.  pointer to a value's private reference recording object ID.  This is the value's private
  684.  object used to hold the list of CMReference "key"/object ID associations as value data.
  685.  If refDataObject is NULL no "R" is generated.  If not NULL we generate the ID after the
  686.  OPT, PT, or T.  
  687.  
  688.  The function returns the container offset to the value entry entry data (v).  This can
  689.  be remembered by the higher-level caller for possible back patching later.
  690.  
  691.  Note, no consistency checking is done here. It is assumed all error checks have been done
  692.  by the caller, and that by the time this routine is called, it is EXPECTED that a TOC
  693.  entry will be created.  I/O errors can, of course, occur.  They use the setjmp/longjmp
  694.  mechanism defined by cmStartTOCIO().
  695. */
  696.  
  697. CM_ULONG cmWrite1TOCSegment(void *tocIOCtl,
  698.                                                         CM_ULONG  objectID, 
  699.                                                         CM_ULONG  propertyID, 
  700.                                                         CM_ULONG  typeID,
  701.                                                         CM_ULONG  value,
  702.                                                         CM_ULONG  valueLen,
  703.                                                         CM_ULONG  generation,
  704.                                                         CM_USHORT flags,
  705.                                                         TOCObjectPtr   refDataObject)
  706. {
  707.     TOCIOControlPtr t = (TOCIOControlPtr)tocIOCtl;
  708.     CM_UCHAR                 entryCode;
  709.     CM_ULONG                 tocEntryData[MaxEntrySize];
  710.     CMBoolean                newTypeSet;
  711.     
  712.     /* Generate OPT, PT, or T depending on how much has changed.  The first write will,     */
  713.     /* of course, generate an OPT since the "previous" object ID is initicalized to 0.        */
  714.     /* If any of these generate, then a possible "generation" can follow.  This is                 */
  715.     /* described after we get through here.                                                                                                */
  716.     
  717.     /* Note, the "previous" values for the object, propery, and type IDs are saved in the    */
  718.     /* I/O control block.  If we change them here, the new value becomes the "previous"        */
  719.     /* value.  That is how the change is detected.                                                                                */
  720.     
  721.     if (t->prevSegment.objectID != objectID) {                            /* new object...                            */
  722.         t->prevSegment.objectID     = tocEntryData[0] = objectID;
  723.         t->prevSegment.propertyID = tocEntryData[1] = propertyID;
  724.         t->prevSegment.typeID         = tocEntryData[2] = typeID;
  725.         (void)writeTOCEntry(t, NewObject, tocEntryData);            /* ...NewObject O P T                    */
  726.         newTypeSet = true;
  727.     } else if (t->prevSegment.propertyID != propertyID) {        /* same object,new property...*/
  728.         t->prevSegment.propertyID = tocEntryData[0] = propertyID;
  729.         t->prevSegment.typeID         = tocEntryData[1] = typeID;
  730.         (void)writeTOCEntry(t, NewProperty, tocEntryData);        /* ...NewProperty P T                    */
  731.         newTypeSet = true;
  732.     } else if (t->prevSegment.typeID != typeID) {                        /* same obj/prop, new type...    */
  733.         t->prevSegment.typeID         = typeID;
  734.         (void)writeTOCEntry(t, NewType, &t->prevSegment.typeID);/*...NewType T                            */
  735.         newTypeSet = true;
  736.     } else
  737.         newTypeSet = false;                                                                        /* no type was set                        */
  738.     
  739.     /* If OPT, PT, or T was generated, then a generation entry and/or references recording*/
  740.     /* object ID can follow it.  The generation is suppressed whenever the value we would    */
  741.     /* generate is equal to the previous value.  Note that the "previous" value is                 */
  742.     /* invalidated (made 0) whenever the buffer is flushed. This will cause the gnenration*/
  743.     /* to repeat following the first OPT, PT, or T in the next block. This is a concession*/
  744.     /* to direct access implementation of TOC blocks (not our implementation) where it         */
  745.     /* will need to pick up the generation again as it accesses the TOC blocks                         */
  746.     /* individually.                                                                                                                                            */
  747.     
  748.     /* The references recording object ID can similarly only occur after a OPT, PT, or T.    */
  749.     /* All these conditions indicate a new value (data).  Thus we can unconditionally put    */
  750.     /* out the ID at this point if one is provided.  It will NEVER be the same as a             */
  751.     /* previous one since each value that has references has its own private recording        */
  752.     /* object to hold the CMReference key/object ID associations.                                                    */
  753.     
  754.     if (newTypeSet)    {                                                                                /* if OPT, PT, or T, and...        */
  755.         if (generation != t->prevSegment.generation) {                /* ...not same as last time...*/
  756.             t->prevSegment.generation = generation;                            /* ...remember for next time    */
  757.             (void)writeTOCEntry(t, ExplicitGen, &generation);        /* ...g                                                */
  758.         }
  759.         
  760.         if (refDataObject)         {                                                                /* ...R (if one is specified)    */
  761. #ifdef DEBUGBENTO
  762.             printf("Writing refDataObject %x of object %x and property %x\n", refDataObject->objectID, objectID, propertyID);
  763. #endif
  764.             (void)writeTOCEntry(t, ReferenceListID, &refDataObject->objectID);
  765.         }
  766.     }
  767.     
  768.     /* The value offset/length or immediate (v) is always generated.  The flags tell us        */
  769.     /* how to interpret the value and which code to put out.                                                            */
  770.     
  771.     tocEntryData[0] = value;                                                                /* set up value data                    */
  772.     tocEntryData[1] = valueLen;                                                            /*... and its length                    */
  773.     
  774.     if (flags & kCMImmediate) {                                                            /* if immediate data...                */
  775.         if (flags & kCMContinued)                                                         /* ...cont'd always means 4        */
  776.             entryCode = ContdImmediate4;
  777.         else                                                                                                    /* ...not cont'd ==> 0 to 4        */
  778.             switch (valueLen) {
  779.                 case 0:     entryCode = Immediate0; break;
  780.                 case 1:     entryCode = Immediate1; break;
  781.                 case 2:     entryCode = Immediate2; break;
  782.                 case 3:     entryCode = Immediate3; break;
  783.                 default: entryCode = Immediate4; break;
  784.             }
  785.     } else                                                                                                    /* non-immediate...                        */
  786.         entryCode = (CM_UCHAR)((flags & kCMContinued) ? ContdOffset4Len4 : Offset4Len4);
  787.     
  788.     return (writeTOCEntry(t, entryCode, tocEntryData));            /* return offset to the value    */
  789. }
  790.  
  791.  
  792. /*-----------------------------------------------------*
  793.  | cmRead1TOCSegment - read a single TOC value segment |
  794.  *-----------------------------------------------------*
  795.  
  796.  All TOC value segment reading is done with this routine.  This routine interfaces the
  797.  low-level TOC entry buffering routines (whose I/O control block is associated with
  798.  tocIOCtl and allocated by cmStartTOCIO()) with the upper-level writers.  The upper level
  799.  (caller) views all value segments in terms of the fields shown as this routine's
  800.  parameters.  
  801.  
  802.  The container TOC has the syntactic layout described by cmWrite1TOCSegment().  Since this
  803.  syntax describes a TOC layout where nothing is repeated if it doesn't change, this
  804.  routine remembers the unrepeated information are repeats it back to the caller.
  805.  
  806.  The function always returns true until an "eof" is detected, i.e., until all the bytes, 
  807.  whose size was specified to cmStartTOCIO(), are read.  False is returned for the "eof".
  808.  
  809.  Note, refsDataObjectID represents a value's private reference recording object ID.  This
  810.  is the value's private object used to hold the list of CMReference "key"/object ID
  811.  associations as value data.  0x00000000UL is returned in refsDataObjectID if there is no
  812.  recording object and, of course, will be the object ID if it is.
  813. */
  814.  
  815. CMBoolean cmRead1TOCSegment(void *tocIOCtl,
  816.                                                         TOCentry  *tocEntry, 
  817.                                                         CM_ULONG  *refsDataObjectID)
  818. {
  819.     TOCIOControlPtr t = (TOCIOControlPtr)tocIOCtl;
  820.     ContainerPtr        container = t->container;
  821.     CM_UCHAR                 entryCode;
  822.     CM_ULONG                 tocEntryData[MaxEntrySize];
  823.     CMBoolean                newTypeRead;
  824.     jmp_buf                 jmpBuf;
  825.     CM_CHAR                    errStr[15];
  826.     
  827.     /* Is is always assumed that TOC reading is "aligned" on a entry code byte, i.e., it     */
  828.     /* is always the next byte to read and at least one entire entry is consumed.  More        */
  829.     /* entries may be consumed.  Pick up the first code and do some initial checks on it.    */
  830.     
  831.     entryCode = readTOCEntry(t, tocEntryData);                /* get the initial code                            */
  832.  
  833.     #if CMDUMPTOC
  834.     SESSION->currTOCoffset = t->currTOCoffset;                /* announce offset for debug display*/
  835.     #endif
  836.  
  837.     if (entryCode == TOCEOF) return (false);                    /* if "eof", tell caller now                */
  838.     
  839.     if (entryCode != NewObject && t->prevSegment.objectID == 0x00000000UL) { /* 1st time?    */
  840.         memcpy(&jmpBuf, &t->tocIOEnv.firstByte, sizeof(jmp_buf)); 
  841.         Container_Disable(container);
  842.         CMfree(t);
  843.         ERROR2(CM_err_TOCParseErr1, cmltostr(entryCode, -2, true, errStr), CONTAINERNAME);
  844.         longjmp(jmpBuf, 1);
  845.     }
  846.     
  847.     /* Process the initial code for OPT, PT, and T.  Suck up the appropriate amount of        */
  848.     /* ID values if we have one of these and read another entry code which must be either    */
  849.     /* a generation number (g) of value data (v).     If we don't have OPT, PT, or T, only     */
  850.     /* value data may follow and we use the "previous" O P T IDs we aready have.  Note,        */
  851.     /* they must exist in that case, since we did an initial validation check that ensured*/
  852.     /* the first TOC entry was OPT and thus set all the "previous" values.                                */
  853.     
  854.     switch (entryCode) {
  855.         case NewObject:                                                                    /* O P T                                                        */
  856.             tocEntry->objectID = t->prevSegment.objectID = tocEntryData[0];
  857.             tocEntry->propertyID = t->prevSegment.propertyID = tocEntryData[1];
  858.             tocEntry->typeID = t->prevSegment.typeID = tocEntryData[2];
  859.             entryCode = readTOCEntry(t, tocEntryData);
  860.             newTypeRead = true;
  861.             break;
  862.  
  863.         case NewProperty:                                                                /* P T                                                            */
  864.             tocEntry->objectID = t->prevSegment.objectID;
  865.             tocEntry->propertyID = t->prevSegment.propertyID = tocEntryData[0];
  866.             tocEntry->typeID = t->prevSegment.typeID = tocEntryData[1];
  867.             entryCode = readTOCEntry(t, tocEntryData);
  868.             newTypeRead = true;
  869.             break;
  870.         
  871.         case NewType:                                                                        /* T                                                                */
  872.             tocEntry->objectID = t->prevSegment.objectID;
  873.             tocEntry->propertyID = t->prevSegment.propertyID;
  874.             tocEntry->typeID = t->prevSegment.typeID = tocEntryData[0];
  875.             entryCode = readTOCEntry(t, tocEntryData);
  876.             newTypeRead = true;
  877.             break;
  878.         
  879.         default:                                                                                /* use "previous" O P T                            */
  880.             tocEntry->objectID = t->prevSegment.objectID;
  881.             tocEntry->propertyID = t->prevSegment.propertyID;
  882.             tocEntry->typeID = t->prevSegment.typeID;
  883.             newTypeRead = false;                                                    /* no type was read                                    */
  884.             break;
  885.     } /* OPT | PT | T */
  886.     
  887.     /* If we encountered an OPT, PT, or T, then a generation number can follow.  If it         */
  888.     /* does we use it and continue to use it until we see another explicit generation            */
  889.     /* number.  All continued segments will get the same generation number.  It is                 */
  890.     /* guaranteed that we will see an generation number after the first OPT, PT, or T in    */
  891.     /* EACH new block we read.                                                                                                                        */
  892.     
  893.     #if CMDUMPTOC
  894.     SESSION->gotExplicitGen = false;                                    /* tell debuggers no change in gen    */
  895.     #endif
  896.     
  897.     *refsDataObjectID = 0x00000000UL;                                    /* assume no refs recording object    */
  898.     
  899.     if (newTypeRead) {                                                                /* if OPT, PT, or T...                            */
  900.         if (entryCode == ExplicitGen) {                                    /* ...if generation follows...            */
  901.             t->prevSegment.generation = tocEntryData[0];     /* ...save it                                                */
  902.             #if CMDUMPTOC
  903.             SESSION->gotExplicitGen = true;                                /* announce explicit gen seen                */
  904.             #endif
  905.             entryCode = readTOCEntry(t, tocEntryData);        /* ...a v or R better follow it            */
  906.         }
  907.         
  908.         if (entryCode == ReferenceListID) {                            /* ...if recording object ID follows*/
  909.             *refsDataObjectID = tocEntryData[0];                    /* ...return it                                            */
  910.             entryCode = readTOCEntry(t, tocEntryData);        /* ...a value (v) better follow it    */
  911.         }
  912.     }
  913.     
  914.     tocEntry->generation = t->prevSegment.generation;                    /* return appropriate generation nbr*/
  915.     
  916.     /* At this point a data value is expected.  The entry code tells us exactly what we        */
  917.     /* got, i.e., how much data there is and how to set the flags.                                                */
  918.     
  919.     switch (entryCode) {
  920.         case Offset4Len4:                                                                /* 4-byte offset + 4-byte length        */
  921.             tocEntry->value.notImm.value = tocEntryData[0];
  922.             tocEntry->value.notImm.valueLen = tocEntryData[1];
  923.             tocEntry->flags = 0x0000U;
  924.             break;
  925.             
  926.         case ContdOffset4Len4:                                                    /* cont'd 4-byte offset + 4-byte len*/
  927.             tocEntry->value.notImm.value = tocEntryData[0];
  928.             tocEntry->value.notImm.valueLen = tocEntryData[1];
  929.             tocEntry->flags = kCMContinued;
  930.             break;
  931.             
  932.         case ContdOffset8Len4:                                                    /* cont'd 8-byte offset + 4-byte len*/
  933.         case Offset8Len4:                                                                /* 8-byte offset + 4-byte length        */
  934.             memcpy(&jmpBuf, &t->tocIOEnv.firstByte, sizeof(jmp_buf)); 
  935.             Container_Disable(container);
  936.             CMfree(t);
  937.             ERROR1(CM_err_Unsupported1, CONTAINERNAME);        /*      NOT SUPPORTED IN THIS             */
  938.             longjmp(jmpBuf, 2);                                                        /*         IMPLEMENTATION                        */
  939.             break;
  940.             
  941.         case Immediate0:                                                                /* immediate (length = 0)                          */
  942.             tocEntry->value.notImm.value = 0;
  943.             tocEntry->value.notImm.valueLen = 0;
  944.             tocEntry->flags = kCMImmediate;
  945.             break;
  946.             
  947.         case Immediate1:                                                                /* immediate (length = 1)                          */
  948.             tocEntry->value.notImm.value = tocEntryData[0];
  949.             tocEntry->value.notImm.valueLen = 1;
  950.             tocEntry->flags = kCMImmediate;
  951.             break;
  952.             
  953.         case Immediate2:                                                                /* immediate (length = 2)                          */
  954.             tocEntry->value.notImm.value = tocEntryData[0];
  955.             tocEntry->value.notImm.valueLen = 2;
  956.             tocEntry->flags = kCMImmediate;
  957.             break;
  958.             
  959.         case Immediate3:                                                                /* immediate (length = 3)                          */
  960.             tocEntry->value.notImm.value = tocEntryData[0];
  961.             tocEntry->value.notImm.valueLen = 3;
  962.             tocEntry->flags = kCMImmediate;
  963.             break;
  964.             
  965.         case Immediate4:                                                                /* immediate (length = 4)                          */
  966.             tocEntry->value.notImm.value = tocEntryData[0];
  967.             tocEntry->value.notImm.valueLen = 4;
  968.             tocEntry->flags = kCMImmediate;
  969.             break;
  970.             
  971.         case ContdImmediate4:                                                        /* continued immediate (length = 4)    */
  972.             tocEntry->value.notImm.value = tocEntryData[0];
  973.             tocEntry->value.notImm.valueLen = 4;
  974.             tocEntry->flags = (kCMImmediate | kCMContinued);
  975.             break;
  976.         
  977.         default:                                                                                /* "I've fallen and can't get up!"    */
  978.             memcpy(&jmpBuf, &t->tocIOEnv.firstByte, sizeof(jmp_buf)); 
  979.             Container_Disable(container);
  980.             CMfree(t);
  981.             ERROR2(CM_err_TOCParseErr2, cmltostr(entryCode, -2, true, errStr), CONTAINERNAME);
  982.             longjmp(jmpBuf, 3);
  983.     } /* switch */
  984.     
  985.     return (true);                                                                        /* tell caller to keep reading            */
  986. }
  987.  
  988.  
  989. #if TOC1_SUPPORT
  990. /*----------------------------------------------------------------------------------------*
  991.  | buildGlobalNameTable - create global symbol table entries for TOC 1 global name values |
  992.  *----------------------------------------------------------------------------------------*
  993.  
  994.  This routine creates a global name symbol table entry for each value that has a property
  995.  ID that indicates the value is for a global name.
  996.  
  997.  When a format 1 TOC is read in from a container we must build up our memory structures
  998.  exactly as they were when the container was previously written.  That means that, in
  999.  addition to the TOC data structures, we have to build the global name symbol table
  1000.  containing the global name strings for those values. Thus is routine is ONLY called during
  1001.  read-in (and only from cmAppendValue()).  At that time the switch tocFullyReadIn is false,
  1002.  which is a status switch indicating the TOC read is not yet complete.
  1003.  
  1004.  A word of caution!  Remember that this routine is only called during read in of a TOC. But
  1005.  here we must read in a global name.  That will CHANGE the current "seek" position of the
  1006.  next read.  cmReadTOC(), which is the routine that does the TOC reading, always keeps its
  1007.  current TOC offset (the one just read) in tocInputOffset in the container.  We use that to
  1008.  re-seek so that cmReadTOC() is none the wiser.
  1009.  
  1010.  Note, there IS an alternate scheme that will equally work to avoid the additional seeks.
  1011.  That is to build the global name table AFTER the TOC is read in at the same time we walk
  1012.  the entire TOC to add objects to their "master chains" of objects, properties, and types.
  1013.  Indeed, once upon a time, that's exactly what we did do!  But the current scheme is
  1014.  probably (depending on your I/O efficiency -- your milage may vary) more efficient.  Why?
  1015.  Because here the global names are built only when we know we have a global name.  So only
  1016.  those objects are involved.  Second, the final TOC walk for the "master chains" is now
  1017.  only for objects.  This is a LOT more efficient than walking the entire TOC down to the
  1018.  values.
  1019. */
  1020.  
  1021. static CMBoolean CM_NEAR buildGlobalNameTable(TOCValuePtr theValue)
  1022. {
  1023.     GlobalNamePtr  g, newGlobalName;
  1024.     CMBoolean             dup;
  1025.     CM_ULONG             valueLen, offset;
  1026.     TOCValueHdrPtr theValueHdr = theValue->theValueHdr;
  1027.     ContainerPtr      container      = theValueHdr->container;
  1028.     
  1029.     /* Extract the global name container offset and its length from the TOC value...            */
  1030.     
  1031.     offset     = theValue->value.notImm.value;
  1032.     valueLen = theValue->value.notImm.valueLen;
  1033.     
  1034.     /* Allocate a new global name symbol table entry.  Failure is disastrous!                            */
  1035.     
  1036.     if ((newGlobalName = (GlobalNamePtr)CMmalloc(sizeof(GlobalName) + valueLen - 1)) == NULL) {
  1037.         Container_Disable(container);
  1038.         ERROR1(CM_err_NoGNameLoad, CONTAINERNAME);
  1039.         return (false);
  1040.     }
  1041.     
  1042.     /* Seek to the name string and read it in.  Failures are still disastrous!                        */
  1043.     
  1044.     CMfseek(container, offset, kCMSeekSet);            
  1045.     
  1046.     if (CMfread(container, newGlobalName->globalName, sizeof(CM_UCHAR), valueLen) != valueLen) {
  1047.         Container_Disable(container);
  1048.         CMfree(newGlobalName);
  1049.         ERROR1(CM_err_BadGNameRead, CONTAINERNAME);
  1050.         return (false);
  1051.     }
  1052.     
  1053.     /* Since this routine is only called during TOC read in, we use tocInputOffset to seek*/
  1054.     /* back to the next TOC input position.  The TOC reader assumes, rightly so, that the    */
  1055.     /* TOC is contiguous, so it doesn't do seeks.  It doesn't know we did one here to get    */
  1056.     /* the global name.  So we put things back the way they should be.                                        */
  1057.     
  1058.     #if (TOCInputBufSize > 0)                                                            /* buffering TOC input...                */
  1059.         CMfseek(container, cmBufferedIOftell(container->ioBuffer), kCMSeekSet);    
  1060.     #else                                                                                                    /* not buffering TOC input...        */
  1061.         CMfseek(container, container->tocInputOffset + TOCentrySize, kCMSeekSet);    
  1062.     #endif
  1063.     
  1064.     if (*(newGlobalName->globalName + valueLen - 1) != '\0') { /* Now check the read...        */
  1065.         Container_Disable(container);
  1066.         CMfree(newGlobalName);
  1067.         ERROR1(CM_err_NotGName, CONTAINERNAME);
  1068.         return (false);
  1069.     }
  1070.     
  1071.     /* Enter the symbol and check for dups.  We must make sure we're not looking at a         */
  1072.     /* for a deleted value.  Of course, considering the time at which this routine is         */
  1073.     /* called, there shouldn't be any deleted entries!  But, what the hell!                                */
  1074.     
  1075.     g = cmEnterGlobalName(container->globalNameTable, newGlobalName, &dup);
  1076.     
  1077.     if (dup) {                                                                                        /* if we have a real dup...            */
  1078.         Container_Disable(container);
  1079.         /* CMfree(newGlobalName);    */                                                /* ...free in cmEnterGlobalName */
  1080.         ERROR2(CM_err_DupGlobalName, g->globalName, CONTAINERNAME); /* yell                                    */
  1081.         return (false);                                                                            /* but continue if error returns*/
  1082.     }
  1083.     
  1084.     /* We're now happy with what we got! Set the value to point to the entry but retain        */
  1085.     /* the offset for the eventual write out to the container.  Set the flags accordingly.*/
  1086.     /* Echo flags in the value hdr.  Set the back pointer from the global name entry to     */
  1087.     /* the value.                                                                                                                                                    */
  1088.     
  1089.     theValue->value.globalName.globalNameSymbol = g;
  1090.     theValue->value.globalName.offset                   = offset;
  1091.     theValue->flags                                                            = kCMGlobalName;
  1092.     
  1093.     newGlobalName->theValue = theValue;                                        /* set back pointer                            */
  1094.     
  1095.     /* Echo the flags in the value header for this global name value.  Also set the size.    */
  1096.     
  1097.     (void)cmSetTOCValueHdrFlags(theValueHdr, kCMGlobalName);
  1098.     theValueHdr->size = valueLen;                                                    /* this includes null at end        */
  1099.     
  1100.     return (true);                                                                                /* global name entry now built    */
  1101. }
  1102.  
  1103.  
  1104. /*------------------------------------------------------------------------*
  1105.  | readTOCfmt1 - read in a TOC format 1 from a already existing container |
  1106.  *------------------------------------------------------------------------*
  1107.  
  1108.  This routine is called to read in a TOC format 1 from a container which has been opened
  1109.  for input by a call to CMOpenContainer().  The container, offset to TOC, and the size of
  1110.  the TOC are passed.  The function returns true if successfully loaded and false otherwise
  1111.  (this is a safety, since error reports should never return from the error handler).
  1112.  
  1113.  Reading a TOC can be stopped in two possible ways:
  1114.  
  1115.          1. By reading the entire TOC, i.e., tocSize bytes starting at tocOffset.
  1116.         
  1117.         2. By reading only a "private" TOC of an updating container.  This is stuff defined
  1118.              only for the container. That is read during "normal" opening of the container. What 
  1119.              follows are TOC entries for objects to be merged with a target container's TOC.
  1120.              That will be read on a second call to read it AFTER the target is opened.  The
  1121.              updates are then automatically merged with the target's TOC which is inherited.
  1122. */
  1123.  
  1124. static CMBoolean CM_NEAR readTOCfmt1(ContainerPtr container, CM_ULONG tocOffset, CM_ULONG tocSize)
  1125. {
  1126.     TOCObjectPtr      theObject;
  1127.     TOCValueHdrPtr theValueHdr;
  1128.     TOCentry             tocEntry;
  1129.     CM_USHORT             objectFlags, shortGen;
  1130.     CMBoolean             updating = false, gotUpdatingStart = false;
  1131.     CM_ULONG             nbrOfUndefObjects, updatingStart;
  1132.     CM_CHAR                 countStr[12];
  1133.     
  1134.     #if (TOCInputBufSize > 0)                                        /* differnt stuff needed if buffering...    */
  1135.     void                     *ioBuffer = NULL;
  1136.     jmp_buf                 readTOCEnv;
  1137.     #else                                                                                /* ...as opposed to not buffering!                */
  1138.     CM_UCHAR              tocBuffer[TOCentrySize];
  1139.     #endif
  1140.  
  1141.     #if (TOCInputBufSize > 0)                                        /* if buffering...                                                */
  1142.     
  1143.     /* If we are using buffered reads to load the TOC, then the input buffering routines    */
  1144.     /* report their errors and exit via a longjmp on the setjmp/longjmp environment             */
  1145.     /* we are about to define.  This avoids checking for errors explicitly on each read        */
  1146.     /* that we do in here.                                                                                                                                */
  1147.     
  1148.     if (setjmp(readTOCEnv)) {                                        /* set setjmp/longjmp environment vector    */
  1149.         Container_Disable(container);
  1150.         cmReleaseIOBuffer(ioBuffer);                            /* ...if longjmp taken back to here...        */
  1151.         ERROR1(CM_err_BadTOCRead, CONTAINERNAME);    /* ...free buffer and report failure            */
  1152.         return (false);                                                        /* ...false ==> failure                                        */
  1153.     }
  1154.     
  1155.     #endif
  1156.     
  1157.     /* Position to the start of the TOC...                                                                                                */
  1158.     
  1159.     CMfseek(container, tocOffset, kCMSeekSet);
  1160.     
  1161.     /* Allocate the input buffer and prepare for buffered reading of the TOC...                        */
  1162.     
  1163.     #if (TOCInputBufSize > 0)                                        /* if buffering...                                                */
  1164.     ioBuffer = cmUseIOBuffer(container, TOCInputBufSize, (jmp_buf *)&readTOCEnv);
  1165.     if (ioBuffer == NULL) return (false);
  1166.     cmNewBufferedInputData(ioBuffer, NULL, tocSize);
  1167.     #endif
  1168.     
  1169.     /* At all time while we're reading a TOC, the container variable, tocInputOffset,         */
  1170.     /* contains the current TOC offset of the entry being processed.  This is used in            */
  1171.     /* determining whether to "split" the TOC read for updating between the public and        */
  1172.     /* private sections.  It's also good to know anyway, so we always know where we are     */
  1173.     /* the TOC read.                                                                                                                                          */
  1174.     
  1175.     container->tocInputOffset = tocOffset;
  1176.     
  1177.     /* Also keep the ioBuffer we're using for the case where we encounter a global name.    */
  1178.     /* These names must be read to put them in the global name table.  That will change     */
  1179.     /* the current input position.  The ioBuffer must be know so that it can be reseeked    */
  1180.     /* back to the current buffered input position.                                                                                */
  1181.     
  1182.     #if (TOCInputBufSize > 0)                                        /* if buffering...                                                */
  1183.     container->ioBuffer = ioBuffer;                            /* ..."broadcast" the buffer we're using    */
  1184.     #else                                                                                /* if no buffering...                                            */
  1185.     container->ioBuffer = NULL;                                    /* ..."broadcast" that too                                */
  1186.     #endif
  1187.     
  1188.     /* As object ID's are encountered during the reading we maintain a counter,                        */
  1189.     /* nbrOfUndefObjects, representing the number of undefined objects.  Objects can be     */
  1190.     /* forward or backward referenced and we can have multiple references.  Thus we have     */
  1191.     /* to be carfull how we handle this counter.  A special flag in the object is used to    */
  1192.     /* ensure we don't count undefined objects more than once and to not count defined        */
  1193.     /* objects.  The counter is set initially, of course, to 0...                                                    */
  1194.     
  1195.     nbrOfUndefObjects = 0;                                    /* hopefully it will be 0 at the end too!            */
  1196.             
  1197.     /* Read in the TOC, one object definition at a time.  For each object we will get a     */
  1198.     /* object ID (what a suprise!), a property ID, and a type ID.  All three IDs are             */
  1199.     /* used to create objects.  The object ID may or may not be a property or type.  We        */
  1200.     /* don't know that here.  The property and type IDs are known to be such because of        */
  1201.     /* where they are extracted for the TOC object.  But all we know is that these are         */
  1202.     /* for property and type objects.  We don't know their full object definitions until    */
  1203.     /* we actually encounter them in the TOC load.  All this forward (and backward) ID        */
  1204.     /* referencing is handled by the object definition routine, cmDefineObject(). All we     */
  1205.     /* do here is define the IDs as objects for each ID number seen.  The property and        */
  1206.     /* type IDs are always considered as undefined objects here.  cmDefineObject() will     */
  1207.     /* handle the forward refrences behind our backs.  So here it goes...                                    */
  1208.     
  1209.     while (tocSize > 0) {                                        /* for each entry in the container's TOC...        */
  1210.         #if (TOCInputBufSize > 0)                            /* if buffering, extract fields here...                */
  1211.         BufferedExtractTOC(ioBuffer, tocEntry.objectID,
  1212.                                                                  tocEntry.propertyID,
  1213.                                                                  tocEntry.typeID,
  1214.                                                                  tocEntry.value.notImm.value,
  1215.                                                                  tocEntry.value.notImm.valueLen,
  1216.                                                                  shortGen,
  1217.                                                                  tocEntry.flags);
  1218.         tocEntry.generation = (CM_ULONG)shortGen;
  1219.         #else                                                                    /* if not buffering, extract directly...            */
  1220.         if (CMfread(container, tocBuffer, sizeof(CM_UCHAR), TOCentrySize) != TOCentrySize) {
  1221.             Container_Disable(container);
  1222.             ERROR1(CM_err_BadTOCRead, CONTAINERNAME);
  1223.             return (false);
  1224.         }
  1225.         ExtractTOC(tocBuffer, tocEntry.objectID,
  1226.                                                     tocEntry.propertyID,
  1227.                                                     tocEntry.typeID,
  1228.                                                     tocEntry.value.notImm.value,
  1229.                                                     tocEntry.value.notImm.valueLen,
  1230.                                                     shortGen,
  1231.                                                     tocEntry.flags);
  1232.         tocEntry.generation = (CM_ULONG)shortGen;
  1233.         #endif
  1234.                 
  1235.         /* Look for an updating container's "pointing" value that points to this container's*/
  1236.         /* target.  If it's there we know we're reading a "private" TOC of the updating            */
  1237.         /* container because the pointing value is a property of TOC #1.  Under these                */
  1238.         /* conditions we want only want to read the private TOC.  What follows are updating    */
  1239.         /* TOC entries (if any) that are to be merged with the target. That will be done by */
  1240.         /* a second read.  The updating TOC entries are defined by another TOC #1 property.    */
  1241.         /* That gives us the starting offset and size.  Using this offset we know where the    */
  1242.         /* provate TOC ends.  Note, there is no TOC #1 in the updates, so we will read to     */
  1243.         /* the end of the TOC on the second read.                                                                                        */
  1244.         
  1245.         if (tocEntry.objectID == CM_StdObjID_TOC)                            /* if looking at TOC #1...        */
  1246.             if (tocEntry.propertyID == CM_StdObjID_TOC_Target)     /* if "pointing" value...            */
  1247.                 updating = true;                                                                    /* ...we have an updating TOC    */
  1248.             else if (tocEntry.propertyID == CM_StdObjID_TOC_NewValuesTOC) {/*if updating TOC..*/
  1249.                 updatingStart = tocEntry.value.imm.ulongValue;        /* ...save updating TOC start    */
  1250.                 gotUpdatingStart = true;                                                    /* ...and that we got it            */
  1251.             }
  1252.             
  1253.         if (updating && gotUpdatingStart)                                            /* if updating TOC...                     */
  1254.             if (container->tocInputOffset >= updatingStart)            /* ...don't read updates now    */
  1255.                     break;
  1256.         
  1257.         /* Define the object.  If the property ID is for a the standard type or property         */
  1258.         /* global name, the flags are set accordingly.  Everything else is treated as a         */
  1259.         /* plain object. It may turn out to be a property or type, but it is not in terms     */
  1260.         /* of a global name.  Also see comments above.                                                                            */
  1261.         
  1262.         if (tocEntry.propertyID == CM_StdObjID_GlobalTypeName)
  1263.             objectFlags = TypeObject;
  1264.         else if (tocEntry.propertyID == CM_StdObjID_GlobalPropName)
  1265.             objectFlags = PropertyObject;
  1266.         else
  1267.             objectFlags = ObjectObject;
  1268.             
  1269.         theObject = cmDefineObject(container, tocEntry.objectID, tocEntry.propertyID,
  1270.                                                               tocEntry.typeID, &tocEntry.value, tocEntry.generation,
  1271.                                                               tocEntry.flags, objectFlags, &theValueHdr);
  1272.         if (theObject == NULL) return (false);
  1273.     
  1274.         /* If this object was previously counted as undefined, reduce the count...                    */
  1275.     
  1276.         if ((theObject->objectFlags & UndefObjectCounted) != 0) {
  1277.             theObject->objectFlags &= ~UndefObjectCounted;
  1278.             --nbrOfUndefObjects;
  1279.         }
  1280.         
  1281.         /* Link the object to its appropriate "master" chains.  The TOC is always sorted by */
  1282.         /* ascending object ID's.  Thus we know that we are linking these things in order.    */
  1283.         /* Note that type and property global names are ALWAYS marked as such by their            */
  1284.         /* property ID (we used that fact to set to flags above).  Thus these will go to         */
  1285.         /* their appropriate master chains.  See cmLinkObject() for details on the master     */
  1286.         /* chain links.                                                                                                                                            */
  1287.         
  1288.         cmLinkObject(container->toc, theObject, NULL);
  1289.         
  1290.         /* If the TOC value entry just defined is for the special "updates" property, then     */
  1291.         /* the object owning this property is a set of updates to be applied to a target         */
  1292.         /* TOC.  In this context we're already using that TOC.  The updates have to be             */
  1293.         /* applied to the "old" objects.  Pure new values just merge in.  But all other         */
  1294.         /* updates (e.g., moving, editing, etc.) are done by interpreting updating                    */
  1295.         /* "instructions".  Those instructions are value data for the special "updates"            */
  1296.         /* property.  However, we can't process them now.  We have to wait until the entire    */
  1297.         /* TOC is read in because the instructions could forward reference yet-to-be-read        */
  1298.         /* objects.  So, if the property is there, we chain those objects together on the        */
  1299.         /* touched chain.  This chain is normally used for recording objects touched for         */
  1300.         /* new updates. But that's later. Here we can overload it and use the same chaining    */
  1301.         /* mechanisms.  Also, to make it easier for the chain walker, cmApplyUpdates(), to     */
  1302.         /* get at the property value, the object's refCon is set with the value "refNum".        */
  1303.         
  1304.         if (tocEntry.propertyID == CM_StdObjID_ValueUpdates)    /* if "updates" property...        */
  1305.             if ((theObject->objectFlags & TouchedObject) == 0) {/* ...add to touched chain...    */
  1306.                 cmAddObjToTouchedChain(container, theObject);    
  1307.                 theObject->objectRefCon = (CMRefCon)theValueHdr;    /* ...save refNum to value        */
  1308.             }
  1309.             
  1310.         /* As mentioned above we always know when we have type and property objects by their*/
  1311.         /* property ID's.  Anything not so marked must be a "garden-variety" object.  Such     */
  1312.         /* an object may be forward referencing to yet-to-be-defined type and property             */
  1313.         /* objects.  We know which is which, by the referencing type and property fields in */
  1314.         /* the TOC entry. So what we do is "declare" (create) these objects as undefined but*/
  1315.         /* marked as to what they are (type or property).  This is an added check so that        */
  1316.         /* when we actually encounter these objects we can ask if they actually are a type    */
  1317.         /* or property.                                                                                                                                            */
  1318.         
  1319.         /* Define a (undefined) property user object...                                                                            */
  1320.         
  1321.         if (tocEntry.propertyID >= MinUserObjectID) {
  1322.             theObject = cmDefineObject(container, tocEntry.propertyID, 0, 0, NULL, 0, 0,
  1323.                                                                  UndefinedObject | PropertyObject, NULL);
  1324.             if (theObject == NULL) return (false);
  1325.             
  1326.             /* If this object actually is undefined, count it as such (but only once)...            */
  1327.             
  1328.             if (theObject->objectFlags & UndefinedObject)
  1329.                 if ((theObject->objectFlags & UndefObjectCounted) == 0) {
  1330.                     theObject->objectFlags |= UndefObjectCounted;
  1331.                     ++nbrOfUndefObjects;
  1332.                 }
  1333.         }
  1334.         
  1335.         /* Define a (undefined) type user object...                                                                                    */
  1336.         
  1337.         if (tocEntry.typeID >= MinUserObjectID) {
  1338.             theObject = cmDefineObject(container, tocEntry.typeID, 0, 0, NULL, 0, 0,
  1339.                                                                  UndefinedObject | TypeObject, NULL);
  1340.             if (theObject == NULL) return (false);
  1341.             
  1342.             /* If this object actually is undefined, count it as such (but only once)...            */
  1343.             
  1344.             if (theObject->objectFlags & UndefinedObject)
  1345.                 if ((theObject->objectFlags & UndefObjectCounted) == 0) {
  1346.                     theObject->objectFlags |= UndefObjectCounted;
  1347.                     ++nbrOfUndefObjects;
  1348.                 }
  1349.         }
  1350.         
  1351.         tocSize -= TOCentrySize;                                        /* count down the size                                    */
  1352.         container->tocInputOffset += TOCentrySize;    /* ...and bump offset                                        */
  1353.     } /* while */
  1354.     
  1355.     #if (TOCInputBufSize > 0)
  1356.     cmReleaseIOBuffer(ioBuffer);                                    /* done with input buffer                                */
  1357.     #endif
  1358.     
  1359.     /* We're done reading.  Report an error if there are any undefined objects...                    */
  1360.     
  1361.     if (nbrOfUndefObjects > 0) {
  1362.         Container_Disable(container);
  1363.         ERROR2(CM_err_UndefObjects, cmltostr(nbrOfUndefObjects, 1, false, countStr), CONTAINERNAME);
  1364.         return (false);
  1365.     }
  1366.     
  1367.     return (true);
  1368. }
  1369. #endif
  1370.  
  1371.  
  1372. /*------------------------------------------------------------------------------------*
  1373.  | cmBuildGlobalNameTable - create global symbol table entries for global name values |
  1374.  *------------------------------------------------------------------------------------*
  1375.  
  1376.  This routine creates a global name symbol table entry for each value that has a property
  1377.  ID that indicates the value is for a global name.
  1378.  
  1379.  When a TOC is read in from a container we must build up our memory structures exactly as
  1380.  they were when the container was previously written.  That means that, in addition to the
  1381.  TOC data structures, we have to build the global name symbol table containing the global
  1382.  name strings for those values. Thus is routine is ONLY called during read-in (and only
  1383.  from cmAppendValue()).  At that time the switch tocFullyReadIn is false, which is a status
  1384.  switch indicating the TOC read is not yet complete.
  1385.  
  1386.  A word of caution!  Remember that this routine is only called during read in of a TOC. But
  1387.  here we must read in a global name.  That will CHANGE the current "seek" position of the
  1388.  next read.  cmReadTOC(), which is the routine that does the TOC reading, always keeps its
  1389.  current TOC offset (the one just read) in tocInputOffset in the container.  We use that to
  1390.  re-seek so that cmReadTOC() is none the wiser.
  1391.  
  1392.  Note, there IS an alternate scheme that will equally work to avoid the additional seeks.
  1393.  That is to build the global name table AFTER the TOC is read in at the same time we walk
  1394.  the entire TOC to add objects to their "master chains" of objects, properties, and types.
  1395.  Indeed, once upon a time, that's exactly what we did do!  But the current scheme is
  1396.  probably (depending on your I/O efficiency -- your milage may vary) more efficient.  Why?
  1397.  Because here the global names are built only when we know we have a global name.  So only
  1398.  those objects are involved.  Second, the final TOC walk for the "master chains" is now
  1399.  only for objects.  This is a LOT more efficient than walking the entire TOC down to the
  1400.  values.
  1401. */
  1402.  
  1403. CMBoolean cmBuildGlobalNameTable(TOCValuePtr theValue)
  1404. {
  1405.     GlobalNamePtr  g, newGlobalName;
  1406.     CMBoolean             dup;
  1407.     CM_ULONG             valueLen, offset;
  1408.     TOCValueHdrPtr theValueHdr = theValue->theValueHdr;
  1409.     ContainerPtr      container      = theValueHdr->container;
  1410.     
  1411.     USE_TOC_FORMAT_1_ALTERNATIVE(buildGlobalNameTable,(theValue));
  1412.  
  1413.     /* Extract the global name container offset and its length from the TOC value...            */
  1414.     
  1415.     offset     = theValue->value.notImm.value;
  1416.     valueLen = theValue->value.notImm.valueLen;
  1417.     
  1418.     /* Allocate a new global name symbol table entry.  Failure is disastrous!                            */
  1419.     
  1420.     if ((newGlobalName = (GlobalNamePtr)CMmalloc(sizeof(GlobalName) + valueLen - 1)) == NULL) {
  1421.         Container_Disable(container);
  1422.         ERROR1(CM_err_NoGNameLoad, CONTAINERNAME);
  1423.         return (false);
  1424.     }
  1425.     
  1426.     /* Seek to the name string and read it in.  Failures are still disastrous!                        */
  1427.     
  1428.     CMfseek(container, offset, kCMSeekSet);            
  1429.     
  1430.     if (CMfread(container, newGlobalName->globalName, sizeof(CM_UCHAR), valueLen) != valueLen) {
  1431.         Container_Disable(container);
  1432.         CMfree(newGlobalName);
  1433.         ERROR1(CM_err_BadGNameRead, CONTAINERNAME);
  1434.         return (false);
  1435.     }
  1436.     
  1437.     /* Since this routine is only called during TOC read in, we must seek BACK to the         */
  1438.     /* next TOC input position.  The TOC input routines assume, rightly so, that the TOC     */
  1439.     /* is contiguous, so it doesn't do seeks.  It doesn't know we did one here to get            */
  1440.     /* the global name.  So we put things back the way they should be.                                        */
  1441.     
  1442.     CMfseek(container, cmFtellTOC(container->tocIOCtl), kCMSeekSet);    
  1443.     
  1444.     if (*(newGlobalName->globalName + valueLen - 1) != '\0') { /* Now check the read...        */
  1445.         Container_Disable(container);
  1446.         CMfree(newGlobalName);
  1447.         ERROR1(CM_err_NotGName, CONTAINERNAME);
  1448.         return (false);
  1449.     }
  1450.     
  1451.     /* Enter the symbol and check for dups.  We must make sure we're not looking at a         */
  1452.     /* for a deleted value.  Of course, considering the time at which this routine is         */
  1453.     /* called, there shouldn't be any deleted entries!  But, what the hell!                                */
  1454.     
  1455.     g = cmEnterGlobalName(container->globalNameTable, newGlobalName, &dup);
  1456.     
  1457.     if (dup) {                                                                                        /* if we have a real dup...            */
  1458.         Container_Disable(container);
  1459.         /* CMfree(newGlobalName);    */                                                /* ...free in cmEnterGlobalName */
  1460.         ERROR2(CM_err_DupGlobalName, g->globalName, CONTAINERNAME); /* yell                                    */
  1461.         return (false);                                                                            /* but continue if error returns*/
  1462.     }
  1463.     
  1464.     /* We're now happy with what we got! Set the value to point to the entry but retain        */
  1465.     /* the offset for the eventual write out to the container.  Set the flags accordingly.*/
  1466.     /* Echo flags in the value hdr.  Set the back pointer from the global name entry to     */
  1467.     /* the value.                                                                                                                                                    */
  1468.     
  1469.     theValue->value.globalName.globalNameSymbol = g;
  1470.     theValue->value.globalName.offset                   = offset;
  1471.     theValue->flags                                                            = kCMGlobalName;
  1472.     
  1473.     newGlobalName->theValue = theValue;                                        /* set back pointer                            */
  1474.     
  1475.     /* Echo the flags in the value header for this global name value.  Also set the size.    */
  1476.     
  1477.     (void)cmSetTOCValueHdrFlags(theValueHdr, kCMGlobalName);
  1478.     theValueHdr->size = valueLen;                                                    /* this includes null at end        */
  1479.     theValueHdr->valueRefCon = container->toc;                        /* remember we read it in                */    
  1480.     
  1481.     return (true);                                                                                /* global name entry now built    */
  1482. }
  1483.  
  1484. /*-------------------------------------------------------------*
  1485.  | cmReadTOC - read in a TOC from a already existing container |
  1486.  *-------------------------------------------------------------*
  1487.  
  1488.  This routine get 1 Toc segment either from the TOC entry list or read from the TOC.
  1489.  If it get it from the TOC entry list, it would get it from the front and free the
  1490.  item afterwards
  1491. */
  1492.  
  1493. static CMBoolean CM_NEAR get1TOCSegment(Boolean useTocList, ContainerPtr container, 
  1494.                                                                                 ListHdr *newEntryList, TOCentry  *tocEntry, 
  1495.                                                                                 CM_ULONG  *refsDataObjectID)
  1496. {
  1497.     TOCEntryListItem    *firstItem;
  1498.     
  1499.     if (useTocList) {
  1500.         firstItem = (TOCEntryListItem *)cmGetListHead(newEntryList);
  1501.         if (firstItem) {                                                            /* we have items in the list                    */
  1502.             memcpy(tocEntry, &firstItem->tocEntryItem, sizeof(TOCentry));
  1503.             *refsDataObjectID = firstItem->refsDataObjectID;
  1504.             cmDeleteListCell(newEntryList, firstItem);    /* info extracted, don't need it            */
  1505.             CMfree(firstItem);                                                    /* we can then free it                                */
  1506.             return true;
  1507.         } else
  1508.             return false;                                                                /* we have read all items in the list */
  1509.     } else
  1510.         return cmRead1TOCSegment(container->tocIOCtl, tocEntry, refsDataObjectID);
  1511. }
  1512.  
  1513. /*-------------------------------------------------------------*
  1514.  | cmReadTOC - read in a TOC from a already existing container |
  1515.  *-------------------------------------------------------------*
  1516.  
  1517.  This routine is called to read in a TOC from a container which has been opened for input
  1518.  by a call to CMOpenContainer().  The container, offset to TOC, and the size of the TOC
  1519.  are passed.  The function returns true if successfully loaded and false otherwise (this
  1520.  is a safety, since error reports should never return from the error handler).
  1521.  
  1522.  Note, the entire TOC whose size is defined by tocSize will be read.  It is an error if
  1523.  not all the bytes are read.  This means that it is expected that tocSize be the EXACT 
  1524.  amount to read from tocOffset.  This is emphasized because, for updating, two TOCs must
  1525.  be read; the private TOC, and the non-private (updating) TOC.  The private offset and
  1526.  size are defined in the container label (by definition).  For non-updating containers,
  1527.  this will reflect the entire (only) TOC.  The non-private (updating) portion is defined
  1528.  by a TOC #1 property.
  1529. */
  1530.  
  1531. CMBoolean cmReadTOC(ContainerPtr container, TocEntryType whichKind, 
  1532.                                         CM_ULONG tocOffset,  CM_ULONG tocSize,
  1533.                                         ListHdr *newEntryList)
  1534. {
  1535.     TOCObjectPtr      theObject;
  1536.     TOCValueHdrPtr theValueHdr;
  1537.     TOCentry             tocEntry;
  1538.     CM_USHORT             objectFlags;
  1539.     CM_ULONG             nbrOfUndefObjects, refsDataObjectID;
  1540.     CM_CHAR                 countStr[12];
  1541.     jmp_buf                 readEnv;
  1542.     TOCEntryListItem *newEntry;
  1543.     CMBoolean             useTocList;        /* true ==> use newEntryList; false ==> read from TOC        */
  1544.         
  1545.     /* read from entryList if it exists and we just want the new entries                                    */
  1546.  
  1547.     useTocList = (newEntryList) && (whichKind == TocEntry_NewEntry);
  1548.     
  1549.     USE_TOC_FORMAT_1_ALTERNATIVE(readTOCfmt1,(container, tocOffset, tocSize));
  1550.     
  1551.     /* Set up current environment for jmpbuf to be restored by the TOC I/O routines if        */
  1552.     /* they detect an error.  The error will have already been reported and the TOC I/O        */
  1553.     /* control block with its buffer freed if the jump is taken.  Thus all we have to do    */
  1554.     /* here is return the bad news to the caller.                                                                                    */
  1555.     
  1556.     if (setjmp(readEnv))                                         /* set setjmp/longjmp environment vector            */
  1557.         return (false);                                                /* ...just quit if there's a TOC input error    */
  1558.     
  1559.     if (!useTocList) {                                            /* read from TOC, then get it ready                        */
  1560.         /* Position to the start of the TOC...                                                                                            */
  1561.     
  1562.         CMfseek(container, tocOffset, kCMSeekSet);
  1563.  
  1564.         /* Define a TOC I/O control block and buffer. The control block pointer is saved in */
  1565.         /* the container mainly for cmBuildGlobalNameTable() to use when it defines global     */
  1566.         /* names and needs to reseek to the current TOC input position. Note, the size of     */
  1567.         /* the TOC buffer required for reading this TOC is as defined in the container            */
  1568.         /* label was squirreled away at open time.                                                                                    */
  1569.     
  1570.         container->tocIOCtl = cmStartTOCIO(container, container->tocBufSize, 
  1571.                                                                              (jmp_buf *)&readEnv, tocOffset, tocSize);
  1572.         if (container->tocIOCtl == NULL) 
  1573.             return (false);                                                /* something went wrong (too bad)!                    */
  1574.     }
  1575.     
  1576.     /* As object ID's are encountered during the reading we maintain a counter,                        */
  1577.     /* nbrOfUndefObjects, representing the number of undefined objects.  Objects can be     */
  1578.     /* forward or backward referenced and we can have multiple references.  Thus we have     */
  1579.     /* to be carfull how we handle this counter.  A special flag in the object is used to    */
  1580.     /* ensure we don't count undefined objects more than once and to not count defined        */
  1581.     /* objects.  The counter is set initially, of course, to 0...                                                    */
  1582.     
  1583.     nbrOfUndefObjects = 0;                                    /* hopefully it will be 0 at the end too!            */
  1584.             
  1585.     /* Read in the TOC, one object definition at a time.  For each object we will get a     */
  1586.     /* object ID (what a suprise!), a property ID, and a type ID.  All three IDs are             */
  1587.     /* used to create objects.  The object ID may or may not be a property or type.  We        */
  1588.     /* don't know that here.  The property and type IDs are known to be such because of        */
  1589.     /* where they are extracted for the TOC object.  But all we know is that these are         */
  1590.     /* for property and type objects.  We don't know their full object definitions until    */
  1591.     /* we actually encounter them in the TOC load.  All this forward (and backward) ID        */
  1592.     /* referencing is handled by the object definition routine, cmDefineObject(). All we     */
  1593.     /* do here is define the IDs as objects for each ID number seen.  The property and        */
  1594.     /* type IDs are always considered as undefined objects here.  cmDefineObject() will     */
  1595.     /* handle the forward refrences behind our backs.  So here it goes...                                    */
  1596.     
  1597.     /* Oh, by the way, when calling cmRead1TOCSegment() to read TOC entries, it will only    */
  1598.     /* return false to terminate the read when ALL the TOC entries have been read (whose     */
  1599.     /* total size was passed to cmStartTOCIO() above). Hence we do not have to check that    */
  1600.     /* we read it all.                                                                                                                                        */
  1601.     
  1602.     while (get1TOCSegment(useTocList, container,                         /* get each entry in TOC...        */
  1603.                                                     newEntryList, &tocEntry, &refsDataObjectID)) {
  1604.         
  1605.         /* Define the object.  If the property ID is for a the standard type or property         */
  1606.         /* global name, the flags are set accordingly.  Everything else is treated as a         */
  1607.         /* plain object. It may turn out to be a property or type, but it is not in terms     */
  1608.         /* of a global name.  Also see comments above.                                                                            */
  1609.         
  1610.         if (tocEntry.propertyID == CM_StdObjID_GlobalTypeName) {
  1611.             objectFlags = TypeObject;
  1612.             if (whichKind == TocEntry_NewEntry) continue;    /* don't register prop in phrase 2 */
  1613.         } else if (tocEntry.propertyID == CM_StdObjID_GlobalPropName) {
  1614.             objectFlags = PropertyObject;
  1615.             if (whichKind == TocEntry_NewEntry) continue;    /* don't register type in phrase 2 */
  1616.         } else {
  1617.             objectFlags = ObjectObject;
  1618.             if (whichKind != TocEntry_All)                        /* always do it, so don't need to check */
  1619.                 /* value update only in phrase 1, nonvalue update only in phrase 2                            */ 
  1620.                 if (tocEntry.propertyID == CM_StdObjID_ValueUpdates) {
  1621.                     if (whichKind == TocEntry_NewEntry) continue;
  1622.                 } else {
  1623.                     if (whichKind == TocEntry_Update) {
  1624.                         if (newEntryList) {
  1625.                             newEntry = (TOCEntryListItemPtr)CMmalloc(sizeof(TOCEntryListItem));
  1626.                             if (!newEntry) return (false);        /* no memory left, report failure                */
  1627.                             memcpy(&newEntry->tocEntryItem, &tocEntry, sizeof(TOCentry));
  1628.                             newEntry->refsDataObjectID = refsDataObjectID;
  1629.                             cmAppendListCell(newEntryList, newEntry);
  1630.                         }
  1631.                         continue;
  1632.                     }
  1633.                 }
  1634.         }    
  1635.         
  1636.         theObject = cmDefineObject(container, tocEntry.objectID, tocEntry.propertyID,
  1637.                                                              tocEntry.typeID, &tocEntry.value, tocEntry.generation,
  1638.                                                              tocEntry.flags, objectFlags, &theValueHdr);
  1639.         if (theObject == NULL) return (false);
  1640.     
  1641.         /* If this object was previously counted as undefined, reduce the count...                    */
  1642.     
  1643.         if ((theObject->objectFlags & UndefObjectCounted) != 0) {
  1644.             theObject->objectFlags &= ~UndefObjectCounted;
  1645.             --nbrOfUndefObjects;
  1646.         }
  1647.         
  1648.         /* Link the object to its appropriate "master" chains.  The TOC is always sorted by */
  1649.         /* ascending object ID's.  Thus we know that we are linking these things in order.    */
  1650.         /* Note that type and property global names are ALWAYS marked as such by their            */
  1651.         /* property ID (we used that fact to set to flags above).  Thus these will go to         */
  1652.         /* their appropriate master chains.  See cmLinkObject() for details on the master     */
  1653.         /* chain links.  By the way, the object is linked only once on their respective         */
  1654.         /* chains.                                                                                                                                                    */
  1655.         
  1656.         /* If the object we just defined is a value's private recording object for a value's*/
  1657.         /* references, then we want to protect it (object and its value header) from user     */
  1658.         /* fiddling just to be safe.  We also don't want it linked on to the master chains. */
  1659.         /* That makes it truly private.  The primary "hook" to this object is from a user     */
  1660.         /* value header that contains data with the reference keys.  These keys are used to */
  1661.         /* get their associated object (ID)  and hence the referenced object itself.  It is */
  1662.         /* these associations that are recorded as a list in the recording object's value     */
  1663.         /* data.  The pointer from the user's value to the private object we just defined.    */
  1664.         /* The pointer will be or already is set when the user's value is picked up while        */
  1665.         /* reading.  This is discussed below where the refsDataObjectID returned from the        */
  1666.         /* cmRead1TOCSegment() is processed.                                                                                                */
  1667.         
  1668.         if (theValueHdr->typeID != CM_StdObjID_ObjRefData)         /* if not a recording value...*/
  1669.             cmLinkObject(container->toc, theObject, NULL);            /* ...link it to master chains*/
  1670.         else if ((theValueHdr->valueFlags & ValueProtected) == 0) {    /* if recording value...*/
  1671.             theValueHdr->valueFlags |= ValueProtected;                    /* ...protect it                            */
  1672.             theValueHdr->theProperty->theObject->objectFlags |= ProtectedObject;
  1673.         }
  1674.             
  1675.         /* If the TOC value entry just defined is for the special "updates" property, then     */
  1676.         /* the object owning this property is a set of updates to be applied to a target         */
  1677.         /* TOC.  In this context we're already using that TOC.  The updates have to be             */
  1678.         /* applied to the "old" objects.  Pure new values just merge in.  But all other         */
  1679.         /* updates (e.g., moving, editing, etc.) are done by interpreting updating                    */
  1680.         /* "instructions".  Those instructions are value data for the special "updates"            */
  1681.         /* property.  However, we can't process them now.  We have to wait until the entire    */
  1682.         /* TOC is read in because the instructions could forward reference yet-to-be-read        */
  1683.         /* objects.  So, if the property is there, we chain those objects together on the        */
  1684.         /* touched chain.  This chain is normally used for recording objects touched for         */
  1685.         /* new updates. But that's later. Here we can overload it and use the same chaining    */
  1686.         /* mechanisms.  Also, to make it easier for the chain walker, cmApplyUpdates(), to     */
  1687.         /* get at the property value, the object's refCon is set with the value "refNum".        */
  1688.         
  1689.         if (tocEntry.propertyID == CM_StdObjID_ValueUpdates)    /* if "updates" property...        */
  1690.             if ((theObject->objectFlags & TouchedObject) == 0) {/* ...add to touched chain...    */
  1691.                 cmAddObjToTouchedChain(container, theObject);    
  1692.                 theObject->objectRefCon = (CMRefCon)theValueHdr;    /* ...save refNum to value        */
  1693.             }
  1694.             
  1695.         /* As mentioned above we always know when we have type and property objects by their*/
  1696.         /* property ID's.  Anything not so marked must be a "garden-variety" object.  Such     */
  1697.         /* an object may be forward referencing to yet-to-be-defined type and property             */
  1698.         /* objects.  We know which is which, by the referencing type and property fields in */
  1699.         /* the TOC entry. So what we do is "declare" (create) these objects as undefined but*/
  1700.         /* marked as to what they are (type or property).  This is an added check so that        */
  1701.         /* when we actually encounter these objects we can ask if they actually are a type    */
  1702.         /* or property.                                                                                                                                            */
  1703.         
  1704.         /* Define a (undefined) property user object...                                                                            */
  1705.         
  1706.         if (tocEntry.propertyID >= MinUserObjectID) {
  1707.             theObject = cmDefineObject(container, tocEntry.propertyID, 0, 0, NULL, 0, 0,
  1708.                                                                  UndefinedObject | PropertyObject, NULL);
  1709.             if (theObject == NULL) return (false);
  1710.             
  1711.             /* If this object actually is undefined, count it as such (but only once)...            */
  1712.             
  1713.             if (theObject->objectFlags & UndefinedObject)
  1714.                 if ((theObject->objectFlags & UndefObjectCounted) == 0) {
  1715.                     theObject->objectFlags |= UndefObjectCounted;
  1716.                     ++nbrOfUndefObjects;
  1717.                 }
  1718.         } /* fwd ref to property */
  1719.         
  1720.         /* Define a (undefined) type user object...                                                                                    */
  1721.         
  1722.         if (tocEntry.typeID >= MinUserObjectID) {
  1723.             theObject = cmDefineObject(container, tocEntry.typeID, 0, 0, NULL, 0, 0,
  1724.                                                                  UndefinedObject | TypeObject, NULL);
  1725.             if (theObject == NULL) return (false);
  1726.             
  1727.             /* If this object actually is undefined, count it as such (but only once)...            */
  1728.             
  1729.             if (theObject->objectFlags & UndefinedObject)
  1730.                 if ((theObject->objectFlags & UndefObjectCounted) == 0) {
  1731.                     theObject->objectFlags |= UndefObjectCounted;
  1732.                     ++nbrOfUndefObjects;
  1733.                 }
  1734.         } /* fwd ref to type */
  1735.         
  1736.         /* Just as we handle possible forward referenced properties and types, we have the    */
  1737.         /* possibility of forward references to a value's recording object for references.    */
  1738.         /* Object references are "pointers" (i.e., object IDs) to other objects from a value*/
  1739.         /* that contains data that refers to those objects.  The data is in the form of a        */
  1740.         /* CMReference "key" and associated object ID.  The associations are maintained in     */
  1741.         /* a list as value data for a uniquely typed value in a private object "tied" to the*/
  1742.         /* value through a pointer in the value header.  It is this private recording object*/
  1743.         /* we define here and tie it to the value (header).                                                                  */
  1744.         
  1745.         /* We know we have a recording object for a value when the cmRead1TOCSegment() we     */
  1746.         /* did above returns a non-zero refsDataObjectID.  The container TOC places the         */
  1747.         /* recording object ID just in front of the value (see cmWrite1TOCSegment() for a        */
  1748.         /* description of the TOC format).  If there is none, 0 is retured.                                    */
  1749.         
  1750.         /* If the recording object is still undefined here, then it will eventually get         */
  1751.         /* defined just like forward referenced types and properties. If it already defined,*/
  1752.         /* so be it.  Either way we get the object pointer so we can hook it to the    value.    */
  1753.  
  1754.         if (refsDataObjectID != 0x00000000UL) {
  1755.             theObject = cmDefineObject(container, refsDataObjectID, 0, 0, NULL, 0, 0,
  1756.                                                                  UndefinedObject | ObjectObject, NULL);
  1757.             if (theObject == NULL) return (false);
  1758.             
  1759.             /* If this object actually is undefined, count it as such (but only once)...            */
  1760.             
  1761.             if (theObject->objectFlags & UndefinedObject)
  1762.                 if ((theObject->objectFlags & UndefObjectCounted) == 0) {
  1763.                     theObject->objectFlags |= UndefObjectCounted;
  1764.                     ++nbrOfUndefObjects;
  1765.                 }
  1766.                 
  1767.             /* Tie the value header to the its recording object...                                                        */
  1768.             
  1769.             RefDataObject(theValueHdr) = theObject;                        /* simple, isn't it?                        */
  1770.         } /* fwd ref recording object */
  1771.     } /* while */
  1772.         
  1773.     if (!useTocList) {                                            /* read from TOC, then clean it up                        */
  1774.         /* We're done reading.  Free the TOC I/O control block and its buffer and report an */
  1775.         /* error if there are any undefined objects...                                                                            */
  1776.         
  1777.         (void)cmEndTOCIO(container->tocIOCtl);
  1778.         container->tocIOCtl = NULL;
  1779.     }
  1780.     
  1781.     if (nbrOfUndefObjects > 0) {
  1782.         Container_Disable(container);
  1783.         ERROR2(CM_err_UndefObjects, cmltostr(nbrOfUndefObjects, 1, false, countStr), CONTAINERNAME);
  1784.         return (false);
  1785.     }
  1786.     
  1787.     return (true);
  1788. }
  1789.  
  1790.  
  1791. /*--------------------------------------------------------------------------*
  1792.  | writeTOCValue - write a TOC container entry for a value to the container |
  1793.  *--------------------------------------------------------------------------*
  1794.  
  1795.  This is used as the "action" parameter to cmWalkThroughEntireTOC() to handle values while
  1796.  writing TOC entries to the container.  The refCon here is a pointer to a communication
  1797.  area (type WriteTOCCommArea) which contains the TOC container entry we have been building
  1798.  up with writeTOCObject(), writeTOCProperty(), and writeTOCValueHdr().  They were called
  1799.  as cmWalkThroughEntireTOC() worked its way through the TOC links.
  1800.  
  1801.  writeTOCValue() is the deepest call in the walk. It is here we add the value, flags, and
  1802.  generation info to the TOC entry and write it to the container.
  1803.  
  1804.  In addition to the TOC entry we are building, the communication area also contains the
  1805.  current total TOC size and offset.  These are updated as we write.
  1806.  
  1807.  Note, we assume the proper positioning here as initiated by cmWriteTOC() which called
  1808.  cmWalkThroughEntireTOC() to start this thing going.  We update the offset assuming it is
  1809.  in sync with the writing.
  1810.  
  1811.  Note, this "static" is intentionally left to default memory model under DOS since it is
  1812.  passed as a function pointer to cmWalkThroughEntireTOC().
  1813. */
  1814.  
  1815. static TOCWalkReturns writeTOCValue(ContainerPtr container, TOCValuePtr theValue, CMRefCon refCon)
  1816. {
  1817.     WriteTOCCommAreaPtr t = (WriteTOCCommAreaPtr)refCon;
  1818.     CM_ULONG                        propertyID, offset;
  1819.     
  1820.     /* Don't write values for standard properties except for the TOC object itself.  To        */
  1821.     /* give us some latitude here, we allow writing of standard properties if their             */
  1822.     /* property ID's are less than or equal to CM_StdObjID_Writable.  There may be some        */
  1823.     /* private properties we might want written.  Using an upper limit means we don't         */
  1824.     /* have to change the following code every time.  Note that the initial test here is     */
  1825.     /* on the object ID.  If we have a user object we ALWAYS write all its properties,        */
  1826.     /* predefined or not.                                                                                                                                    */
  1827.     
  1828.     if (t->tocEntry.objectID >= CM_StdObjID_TOC && t->tocEntry.objectID < MinUserObjectID) {
  1829.         propertyID = theValue->theValueHdr->theProperty->propertyID;
  1830.         if (propertyID > CM_StdObjID_Writable && propertyID < MinUserObjectID) {
  1831.             return (WalkNextTOCValueHdr);
  1832.         }
  1833.     }
  1834.     
  1835.     /* Don't write dynamic values...                                                                                                            */
  1836.     
  1837.     if (t->tocEntry.propertyID == CM_StdObjID_DynamicValues) return (WalkNextTOCValueHdr);
  1838.     
  1839.     /* If we have a global name symbol table entry pointer for the value, then we assume     */
  1840.     /* that by this time the global names have been written to the TOC and the offset         */
  1841.     /* placed in the value data.  We have to use the global name pointer to get the length*/
  1842.     /* of the string.                                                                                                                                            */
  1843.     
  1844.     if (theValue->flags & kCMGlobalName) {
  1845.         t->tocEntry.value.notImm.value         = theValue->value.globalName.offset;
  1846.         t->tocEntry.value.notImm.valueLen = GetGlobalNameLength(theValue->value.globalName.globalNameSymbol) + 1;
  1847.         t->tocEntry.flags                                     = 0;                            /* std value in the container        */            
  1848.     } else {                                                                                            /* normal values handled here...*/
  1849.         t->tocEntry.value = theValue->value;
  1850.         t->tocEntry.flags    = theValue->flags;                                /* set flags as is                            */
  1851.         /* make sure continue flag is set correctly                                                                                    */
  1852.         if (cmGetNextListCell(theValue))
  1853.             t->tocEntry.flags |= kCMContinued;
  1854.         else
  1855.             t->tocEntry.flags &= ~kCMContinued;            /* not really needed, just extra caution     */
  1856.     }
  1857.     
  1858.     /* Write the TOC entry to the container.  The offset returned from this write is to     */
  1859.     /* where the value  is placed in the container.  We use this if the entry requires        */
  1860.     /* back patching.                                                                                                                                            */
  1861.     
  1862.     offset = cmWrite1TOCSegment(container->tocIOCtl,
  1863.                                                             t->tocEntry.objectID,
  1864.                                                             t->tocEntry.propertyID,
  1865.                                                             t->tocEntry.typeID,
  1866.                                                             t->tocEntry.value.notImm.value,
  1867.                                                             t->tocEntry.value.notImm.valueLen,
  1868.                                                             t->tocEntry.generation,
  1869.                                                             t->tocEntry.flags,
  1870.                                                             t->refDataObject);
  1871.     
  1872.     /* Some TOC values are NOT known at this time.  We must remember where they are and        */
  1873.     /* back patch them later after we do know.  The following values currently require        */
  1874.     /* back patching (all of these belong to TOC #1):                                                                            */
  1875.     
  1876.     /*        1. The TOC size.                                                                                                                                */
  1877.     /*        2. The total size of the container including the TOC.                                                        */
  1878.     /*        3. The non-private portion of an updating TOC.                                                                    */
  1879.     
  1880.     if (t->thePatches != NULL)                                                        /* if patches are required...        */
  1881.         if (theValue == container->tocObjValue) {                        /* ...remember appropriate info    */
  1882.             t->thePatches->tocSizeEntry = offset;                            /*        1. TOC size                                */
  1883.             t->thePatches->whatWeGot |= Patch_TOCsize;
  1884.         } else if (theValue == container->tocContainerValue) {
  1885.             t->thePatches->tocContEntry = offset;                            /*        2. entire container                */
  1886.             t->thePatches->whatWeGot |= Patch_TOCContSize;
  1887.         } else if (theValue == container->tocNewValuesValue) {
  1888.             t->thePatches->tocNewValuesTOCEntry = offset;            /*        3. non-private TOC                */
  1889.             t->thePatches->whatWeGot |= Patch_TOCNewValuesTOC;
  1890.         }
  1891.     
  1892.     return (WalkNextTOCValue);                                                        /* continue walking value segs    */
  1893. }
  1894.  
  1895.  
  1896. /*------------------------------------------------------------------*
  1897.  | writeTOCValueHdr - build up a TOC container entry with a type ID |
  1898.  *------------------------------------------------------------------*
  1899.  
  1900.  This is used as the "action" parameter to cmWalkThroughEntireTOC() to handle value header
  1901.  structs while writing TOC entries to the container.  The refCon here is a pointer to a
  1902.  communication area (type WriteTOCCommArea) which contains the TOC container entry we are
  1903.  building up in preparation for writing when a value is reached.  All we need do here is
  1904.  set the type ID in the entry.  cmWalkThroughEntireTOC() will next call writeTOCValue()
  1905.  which is where we will do the actual writing of the TOC entry we have been building up
  1906.  with this routine, writeTOCProperty(), and writeTOCObject() below.
  1907.  
  1908.  Note, this "static" is intentionally left to default memory model under DOS since it is
  1909.  passed as a function pointer to cmWalkThroughEntireTOC().
  1910. */
  1911.  
  1912. static TOCWalkReturns writeTOCValueHdr(ContainerPtr container, TOCValueHdrPtr theValueHdr, CMRefCon refCon)
  1913. {
  1914.     WriteTOCCommAreaPtr t = (WriteTOCCommAreaPtr)refCon;
  1915.     TOCValueBytes              value;
  1916.     CM_CHAR                            idStr[15];
  1917.     ContainerPtr                unused = container;
  1918.     
  1919.     /* Only write TOC entries for the current container.  Values can be marked as                 */
  1920.     /* belonging to other (i.e, updating target) containers.  We don't want those.                */
  1921.         
  1922.     if (theValueHdr->container != container)                                 /* if not current container...*/
  1923.         return (WalkNextTOCValueHdr);                                                    /* ...suppress writing value    */
  1924.  
  1925.     /* If we are looking at the special property for a dynamic value, then we have an            */
  1926.     /* outstanding dynamic value that has not been released with a CMReleaseValue().  It    */
  1927.     /* is required that dynamic values be released COMPLETELY.  That causes the special        */
  1928.     /* property containing the dynamic values for the object to be removed.  We would         */
  1929.     /* then not see them here.  If we do, it's an error...                                                                */
  1930.     
  1931.     /* Well, not always an error!  We "cheat" a little here.  As it turns out we may be        */
  1932.     /* use a dynamic value as a pointing value to a target we're updating.  Its one we        */
  1933.     /* created that the user knows nothing about.  It WILL (trust me) be released, just     */
  1934.     /* not at the time we're writing this TOC.  So we want to ignore that dynamic value.    */
  1935.     /* We can detect we got it because we create its type before we have the target             */
  1936.     /* container (we need it to get the target, so it's a "chicken-and-egg" problem). Such*/
  1937.     /* types will have type IDs less than the min seed value.  That's why we keep a min        */
  1938.     /* seed.  So we can tell what objects "belong" to the updating container and what            */
  1939.     /* objects are updates.                                                                                                                                */
  1940.     
  1941.     if (t->tocEntry.propertyID == CM_StdObjID_DynamicValues) {/* if a dynamic value...        */
  1942.         if (theValueHdr->typeID >= MinSeed)                                            /* ...that's >= MinSeed...    */
  1943.         {    Container_Disable(container);
  1944.             ERROR1(CM_err_NotReleased, CONTAINERNAME);                        /* ...why wasn't it released*/
  1945.         }
  1946.         return (WalkNextTOCValueHdr);                                                        /* ...suppress writing value*/
  1947.     }
  1948.     
  1949.     t->tocEntry.typeID          = theValueHdr->typeID;                        /* set type ID                                */
  1950.     t->tocEntry.generation = theValueHdr->generation;                /* set generation                            */
  1951.     
  1952.     t->refDataObject             = HasRefDataObject(theValueHdr) ? RefDataObject(theValueHdr)
  1953.                                                                                                                  : NULL;
  1954.         
  1955.     /* Make sure that this valueHdr does indeed have a value. When a CMNewValue() is done    */
  1956.     /* an object is created but the value header contains no value list.  At that point     */
  1957.     /* if any of these are still lying around we will see them here. It represents                 */
  1958.     /* an object that was never given a value.  That's a no, no...                                                */
  1959.     
  1960.     if (cmIsEmptyList(&theValueHdr->valueList)) {                        /* error if no value list...    */
  1961.         Container_Disable(container);
  1962.         ERROR3(CM_err_NoValue, cmltostr(theValueHdr->theProperty->theObject->objectID, 1, false, idStr), 
  1963.                                                    cmGetGlobalPropertyName(container, t->tocEntry.propertyID),
  1964.                                                      CONTAINERNAME);
  1965.         (void)cmSetValueBytes(container, &value, Value_NotImm, 0xFFFFFFFFU, 0); /* -1 offset*/
  1966.         if (cmAppendValue(theValueHdr, &value, 0) == NULL)
  1967.             AbortWalkThroughEntireTOC(CM_err_NoValue);                    /* abort if this fails (damit)*/
  1968.     }
  1969.     #if 0
  1970.         else if (theValueHdr->dynValueData.dynValue != NULL)    /* safety for dynamic values    */
  1971.             theValueHdr->dynValueData.dynValue = NULL;
  1972.     #endif
  1973.     
  1974.     return (WalkNextTOCValue);                                                            /* walk the value segments        */
  1975. }
  1976.  
  1977.  
  1978. /*----------------------------------------------------------------------*
  1979.  | writeTOCProperty - build up a TOC container entry with a property ID |
  1980.  *----------------------------------------------------------------------*
  1981.  
  1982.  This is used as the "action" parameter to cmWalkThroughEntireTOC() to handle property
  1983.  structs while writing TOC entries to the container.  The refCon here is a pointer to a
  1984.  communication area (type WriteTOCCommArea) which contains the TOC container entry we are
  1985.  building up in preparation for writing when a value is reached.  All we need do here is
  1986.  set the property ID in the entry.  cmWalkThroughEntireTOC() will next call 
  1987.  writeTOCValueHdr() to do a similar stunt for the type ID.
  1988.  
  1989.  Note, this "static" is intentionally left to default memory model under DOS since it is
  1990.  passed as a function pointer to cmWalkThroughEntireTOC().
  1991. */
  1992.  
  1993. static TOCWalkReturns writeTOCProperty(ContainerPtr container, TOCPropertyPtr theProperty, CMRefCon refCon)
  1994. {
  1995.     WriteTOCCommAreaPtr t = (WriteTOCCommAreaPtr)refCon;
  1996.     ContainerPtr                unused = container;
  1997.         
  1998.     t->tocEntry.propertyID = theProperty->propertyID;
  1999.     
  2000.     return (WalkNextTOCValueHdr);                                                        /* walk value headers                    */
  2001. }
  2002.  
  2003.  
  2004. /*------------------------------------------------------------------*
  2005.  | writeTOCObject - build up a TOC container entry with a object ID |
  2006.  *------------------------------------------------------------------*
  2007.  
  2008.  This is used as the "action" parameter to cmWalkThroughEntireTOC() to handle object
  2009.  structs while writing TOC entries to the container.  The refCon here is a pointer to a
  2010.  communication area (type WriteTOCCommArea) which contains the TOC container entry we are
  2011.  building up in preparation for writing when a value is reached.  All we need do here is
  2012.  set the value ID in the entry.  cmWalkThroughEntireTOC() will next call writeTOCProperty()
  2013.  to do a similar stunt for the property ID.
  2014.  
  2015.  Note, this "static" is intentionally left to default memory model under DOS since it is
  2016.  passed as a function pointer to cmWalkThroughEntireTOC().
  2017. */
  2018.  
  2019. static TOCWalkReturns writeTOCObject(ContainerPtr container, TOCObjectPtr theObject, CMRefCon refCon)
  2020. {
  2021.     WriteTOCCommAreaPtr t = (WriteTOCCommAreaPtr)refCon;
  2022.     ContainerPtr                unused = container;
  2023.     
  2024.     t->tocEntry.objectID = theObject->objectID;
  2025.     
  2026.     return (WalkNextTOCProperty);                                                        /* walk properties                        */
  2027. }
  2028.  
  2029.  
  2030. /*-----------------------------------------*
  2031.  | cmWriteTOC - write a TOC to a container |
  2032.  *-----------------------------------------*
  2033.  
  2034.  This routine is the inverse to cmReadTOC() to write the in-memory TOC to a container. The
  2035.  TOC is written to the end of the container.  Only objects with ID's greater than or equal
  2036.  to the startingID and less than or equal to the endingID are written.  Further, only
  2037.  objects whose value headers are equal to the specified container are written.
  2038.  
  2039.  With the restriction on the container, we will only write to the TOC newly created values
  2040.  for this open session.  If we're not updating, only one container so this is not an
  2041.  issue.  But for updating, only looking at the stuff in the updating container will yield
  2042.  all new objects and all new properties for "old" objects.  We can split these two groups
  2043.  using the ID limits.
  2044.  
  2045.  Note, non-TOC updates are generated by a separate walk not done here.  See Updating.c.
  2046.  
  2047.  For updating, a container may have its own TOC and the target TOC.  Thus the TOC pointer
  2048.  is an explicit parameter.  The container offset is returned in tocStart and the size of
  2049.  the TOC in tocSize.  The function returns true to indicate success and false otherwise
  2050.  (as a safety).
  2051.  
  2052.  There are some TOC entries that must be back-patched. This is because they are a function
  2053.  of the final TOC size in the container.  We don't know that until the entire TOC is
  2054.  written.  At that time we can rewrite the entries with the proper values filled in.
  2055.  
  2056.  Because of updating, there may be multiple calls to cmWriteTOC() to write various subsets
  2057.  of the TOC (i.e., the updates).  Thus we may not know the final TOC size when we return
  2058.  from here.  The back-patching cannot be done.  Only the caller knows when all TOC writes
  2059.  are complete and the back-patching can be done.
  2060.  
  2061.  To allow this to happen, thePatches is passed as a pointer to a BackPatches struct where
  2062.  we save the original unpatched TOC info and offset for the entries of interest.  As these
  2063.  entries are encountered (and they must be sometime) during TOC writing, we save them and
  2064.  their offsets in the struct. When the caller is ready to rewrite them cmDoBackPathes() is
  2065.  called to do it.
  2066.  
  2067.  If thePatches is passed as NULL, nothing (obviously) is saved for back-patching.
  2068. */
  2069.  
  2070. CMBoolean cmWriteTOC(ContainerPtr container, void *toc, CMObjectID startingID,
  2071.                                           CMObjectID endingID, BackPatchesPtr thePatches,
  2072.                                           CM_ULONG *tocStart, CM_ULONG *tocSize,
  2073.                                           CMBoolean truncFreeSpace)
  2074. {
  2075.     WriteTOCCommArea tocCommArea;
  2076.     int                          x;
  2077.     CM_ULONG                 offset, logicalEOF;
  2078. #if !(CMTOPLEVEL_CRASH_PROOF || CMEMBEDDED_CRASH_PROOF)
  2079.     CM_ULONG                 logicalEOF0, slopSize;
  2080. #endif
  2081.     CM_UCHAR                 padding[5], *p;
  2082.     jmp_buf                      writeEnv;
  2083.         
  2084.     /* Set up current environment for jmpbuf to be restored by the TOC I/O routines if        */
  2085.     /* they detect an error.  The error will have already been reported and the TOC I/O        */
  2086.     /* control block with its buffer freed if the jump is taken.  Thus all we have to do    */
  2087.     /* here is return the bad news to the caller.                                                                                    */
  2088.     
  2089.     if (setjmp(writeEnv))                                     /* set setjmp/longjmp environment vector            */
  2090.         return (false);                                                /* ...just quit if there's a TOC output error    */
  2091.         
  2092.     /* Set the "seed" user object ID value in the special TOC entry.  This is picked up        */
  2093.     /* when the container is opened for reading to allow us to continue on using unique        */
  2094.     /* object ID numbers.  The pointer to the value for the special TOC entry was set when*/
  2095.     /* the container was opened.                                                                                                                    */
  2096.     
  2097.     Seed = container->nextUserObjectID;
  2098.         
  2099.     /* We are now ready to write the TOC. Except for space reuse we always append the TOC */
  2100.     /* to the end of the container (i.e., at the physical EOF).  But because of space         */
  2101.     /* reuse we may have to write the TOC twice!  Let me try to explain...                                */
  2102.     
  2103.     /* The reason is that with space reuse we start the TOC at the logical EOF rather         */
  2104.     /* than the physical EOF if it is less than the physical EOF.  CMOpenContainer() sets */
  2105.     /* the logical EOF to the start of the "old" TOC for space reusing updates and the         */
  2106.     /* "old" TOC is redefined as reusable data free space.  That, along with possibly         */
  2107.     /* already exiting free space, may be enough to cover all the updates.  Then we can     */
  2108.     /* start writing the new TOC at the logical EOF to minimize growth of the container.  */
  2109.     /* If it isn't, we append the TOC to the end of the container as usual.                                */
  2110.     
  2111.     /* Note, that the container can NEVER get smaller in this "pure" portable                         */
  2112.     /* implementation of the Container Manager.  The reason is that we assume the I/O            */
  2113.     /* handlers are using standard ANSI stream I/O.  It provides NO facility for cutting    */
  2114.     /* a stream back!  This is important and the reason there are two potential writes of */
  2115.     /* the TOC.                                                                                                                                                        */
  2116.     
  2117.     /* If we indeed do start writing the TOC at the logical EOF, it is possible that the    */
  2118.     /* new TOC is small enough that we don't write enough entries to reach the physical     */
  2119.     /* EOF.  We then have a bunch of space ("slop") from the end of the new TOC to the         */
  2120.     /* physical EOF to account for.  The container looks something like this at this             */
  2121.     /* point (the label is not yet written at this point, but the physical EOF would             */
  2122.     /* follow the old label):                                                                                                                            */
  2123.     
  2124.     /*              *------------------*-----------*----------*- - - - - - -*                            */
  2125.     /*   container: |<----- data ----->|<-- TOC -->|<- slop ->|    label    |physical eof    */
  2126.     /*              *------------------*-----------*----------*    - - - - - - -*                        */
  2127.     
  2128.     /* We cannot leave the slop where it is because we always write the container label     */
  2129.     /* immediately following the TOC.  Thus we must "move" the TOC so that we have the         */
  2130.     /* following configuration:                                                                                                                        */
  2131.     
  2132.     /*              *------------------*----------*-----------*- - - - - - -*                            */
  2133.     /*   container: |<----- data ----->|<- slop ->|<-- TOC -->|    label    |physical eof    */
  2134.     /*              *------------------*----------*-----------*- - - - - - -*                            */
  2135.  
  2136.     /* Unfortunately, there is no easy way to determine the TOC size until we write it, so*/
  2137.     /* there is no way to know the slop ahead of time.  We could do all the same logic as */
  2138.     /* we do to write it and suppress the writing.  But who knows?  We could get lucky         */
  2139.     /* and have no slop.  So we write the TOC to see if the slop occurs.  This is TOC         */
  2140.     /* write number 1.                                                                                                                                      */
  2141.     
  2142.     /* Now if there indeed is slop, we do the "move" by doing the TOC write the second         */
  2143.     /* time (hence the two possible writes).  The slop is first moved to the free list.        */
  2144.     /* This means the second TOC write will be slightly different (and one TOC entry             */
  2145.     /* larger).  This implies that the copy must be done as a second TOC write rather         */
  2146.     /* than by some other means (e.g., like copying the TOC as raw bytes down).                        */
  2147.     
  2148.     /* The following will help define the variables we use for this so you can understand    */
  2149.     /* the math:                                                                                                                                                    */
  2150.     
  2151.     /*                            LogicalEOF0                 x        physicalEOF                */
  2152.     /*                                 |                      |             |                            */
  2153.     /*              *------------------*-----------*----------*- - - - - - -*                            */
  2154.     /*   container: |<----- data ----->|<-- TOC -->|<- slop ->|    label    |                            */
  2155.     /*              *------------------*-----------*----------*- - - - - - -*                            */
  2156.     /*                                             |                                                                            */
  2157.     /*                                         LogicalEOF            |<- LBLsize ->|                            */
  2158.     
  2159.     /*     size of slop = (physicalEOF - LBLsize) - LogicalEOF  (takes label into account)        */
  2160.     
  2161.     /* For the second TOC write to happen, the logical EOF after the first TOC write must    */
  2162.     /* be less then "x" (= physicalEOF - LBLsize).  The slop is added to the free list         */
  2163.     /* with a starting offset of LogicalEOF0.  LogicalEOF is then moved back to                     */
  2164.     /* LogicalEOF0 + slop size.  The second TOC write will thus end at "x" (or one TOC         */
  2165.     /* entry further if the slop was placed uniquely on the free list).                                        */
  2166.     
  2167.     /* Now, having described all this crap, there is a possibility you can forget it!     */
  2168.     /* Remember, that the reason for all of this is due to the limitations of standard         */
  2169.     /* ANSI C I/O.  Specific implementations may have library (or whatever) support for        */
  2170.     /* cutting a stream back. If we could cut it back, we would cut it back to the logical*/
  2171.     /* EOF and write the TOC to the new physical EOF which now equals the logical EOF.        */
  2172.     
  2173.     /* So, on the possibility that such a support exists, a special handler, "trunc", may    */
  2174.     /* be defined to "truncate" the stream.  We test for its presence here. If it exists, */
  2175.     /* we call it and "trust" it to do the truncation. Then we only have to do the single */
  2176.     /* TOC write.                                                                                                                                                    */
  2177.     
  2178.     /* Finally, one last point to keep in mind about all this.  Even if the trunc handler */
  2179.     /* is not supplied, the probability of the two writes is, I think, fairly small!  The    */
  2180.     /* reason comes from the fact of noting how you can get a smaller TOC in the first        */
  2181.     /* place.  The only way it can happen is to free values.  That reduces the number of    */
  2182.     /* TOC entries.  But they are replaced by free list entries.  So, unless free space        */
  2183.     /* coalesces (which it could do), there will be no net loss of TOC entries and hence    */
  2184.     /* the TOC will not get smaller.  So what's the probability of separate values data        */
  2185.     /* space coalescing?  That is basically the probability of the TOC growing smaller.        */
  2186.     
  2187.     /* Whew!                                                                                                                                                            */
  2188.     
  2189.     /* The above is only true if we are willing to take a chance that nothing goes wrong    */
  2190.     /* when we are writing the new TOC. If something goes wrong then the old TOC is             */
  2191.     /* destroyed before the new one is written and the file is not readable, even for the    */
  2192.     /* old content. Of course the chance is very small, but we are still making a chance.    */
  2193.     /* For those who want to be absolutely safe, turn on the CMCRASH_PROOF flag and we        */
  2194.     /* would not try to write over the old TOC.                                                                                        */
  2195.  
  2196. #if !(CMTOPLEVEL_CRASH_PROOF || CMEMBEDDED_CRASH_PROOF)
  2197.     logicalEOF0 = container->logicalEOF;                            /* save original logical EOF                */
  2198.     
  2199.     for (;/*two possible tries*/;) {
  2200. #endif        
  2201.         /* Seek to the start of where we want to put the TOC...                                                            */
  2202.         
  2203.         offset = CMgetContainerSize(container);                    /* this is the next free byte                */
  2204.         if (truncFreeSpace) {
  2205.             offset = ((*tocStart + 3) / 4) * 4;                        /* round start up to multiple of 4    */
  2206.             CMfseek(container, offset, kCMSeekSet);                /* ...position to EOF                                */
  2207.             cmDeleteFreeSpace(container, offset+container->mergeInSize);
  2208.         }
  2209. #if (CMTOPLEVEL_CRASH_PROOF || CMEMBEDDED_CRASH_PROOF)
  2210.         else                                                                                        /* for crash proof, always                    */
  2211.             CMfseek(container, 0, kCMSeekEnd);                        /* ...append TOC to end of container*/
  2212. #else
  2213.         else if (container->logicalEOF >= offset)                /* if we wrote up to this point...    */
  2214.             CMfseek(container, 0, kCMSeekEnd);                        /* ...append TOC to end of container*/
  2215.         else if (container->handler.cmftrunc != NULL &&    /* if truncation handler provided...*/
  2216.                          CMftrunc(container, container->logicalEOF)) {/* ...and it trucated containr*/
  2217.             offset = CMgetContainerSize(container);                /* ...get new truncated size                */
  2218.             container->logicalEOF = offset;                                /* ...logical EOF == physical now        */
  2219.             CMfseek(container, 0, kCMSeekEnd);                        /* ...position to EOF                                */
  2220.             cmDeleteFreeSpace(container, offset);                    /* ...kill free list entries >= EOF */
  2221.         } else {                                                                                /* if we didn't use all (old) space    */
  2222.             offset = container->logicalEOF;                                /* ...overwrite from logical EOF        */
  2223.             CMfseek(container, offset, kCMSeekSet);                /* ...position to EOF                                */
  2224.             cmDeleteFreeSpace(container, offset);                    /* ...kill free list entries >= EOF */
  2225.         }
  2226. #endif        
  2227.         /* Pad the container to align it on a multiple of 4 boundary. We fill the pad bytes    */
  2228.         /* with 0xFF so they are easy to spot during debugging.                                                            */
  2229.         
  2230.         *tocSize = 0;                                                                        /* init size                                                */
  2231.         *tocStart = ((offset + 3) / 4) * 4;                            /* round start up to multiple of 4    */
  2232.         
  2233.         if (offset < *tocStart) {                                                /* if padding is necessary...                */
  2234.             p = padding;                                                                    /* ...create the padding of FF's        */
  2235.             do
  2236.                 *p++ = 0xFF;
  2237.             while (++offset < *tocStart);
  2238.             x = p - padding;
  2239.             if ((int)CMfwrite(container, padding, sizeof(CM_CHAR), x) != x) { /*write the padding*/
  2240.                 Container_Disable(container);
  2241.                 ERROR1(CM_err_BadTOCWrite, CONTAINERNAME);
  2242.                 return (false);
  2243.             }
  2244.         }
  2245.             
  2246.         container->tocOffset = offset;                                    /* announce where TOC starts                */
  2247.  
  2248.         /* Define a TOC I/O control block and buffer. The control block pointer is saved in */
  2249.         /* the container mainly for the TOC walk routines to use when it comes time to write*/
  2250.         /* a TOC entry.                                                                                                                                          */
  2251.         
  2252.         container->tocIOCtl = cmStartTOCIO(container, container->tocBufSize, (jmp_buf *)&writeEnv,
  2253.                                                                              offset, 0);
  2254.         if (container->tocIOCtl == NULL) 
  2255.             return (false);                                                                /* something went wrong (too bad)!    */
  2256.         
  2257.         /* Walk the entire TOC building up and writing each TOC container entry...                    */
  2258.         
  2259.         tocCommArea.thePatches = thePatches;                                     
  2260.         
  2261.         x = cmWalkThroughEntireTOC(container, toc, startingID, endingID, &tocCommArea, 
  2262.                                                              writeTOCObject, writeTOCProperty, writeTOCValueHdr,
  2263.                                                              writeTOCValue);
  2264.         if (x != 0) return (false);                                                            /* something went wrong!        */
  2265.         
  2266.         /* Flush the last TOC buffer and return TOC size to caller...                                                                                                            */
  2267.         
  2268.         *tocSize = container->tocSize = cmFlushTOCOutputBuffer(container->tocIOCtl, true);
  2269.     
  2270.         /* We're done writing.  Free the TOC I/O control block and its buffer.                             */
  2271.         
  2272.         (void)cmEndTOCIO(container->tocIOCtl);
  2273.         container->tocIOCtl = NULL;
  2274.         
  2275.         container->physicalEOF = CMgetContainerSize(container); /* update physical EOF            */
  2276.         logicalEOF = *tocStart + *tocSize;                                            /* ...and the logical EOF        */
  2277.         SetLogicalEOF(logicalEOF);
  2278.         
  2279. #if !(CMTOPLEVEL_CRASH_PROOF || CMEMBEDDED_CRASH_PROOF)
  2280.         /* If we write up to the physical EOF, there is no slop, so we're done...                        */
  2281.         
  2282.         if (container->logicalEOF >= container->physicalEOF - LBLsize) break;
  2283.         
  2284.         /* We got some slop!  Put it on the free list with the original logical EOF as            */
  2285.         /* its position.  The set the logical EOF past it and loop around for the second         */
  2286.         /* TOC write.  That is guaranteed to reach the physical EOF (it better!) so we will    */
  2287.         /* break out of the loop with the statement above.                                                                    */
  2288.         
  2289.         slopSize = (container->physicalEOF - LBLsize) - container->logicalEOF;
  2290.         cmAddToFreeList(container, NULL, logicalEOF0, slopSize);/* put slop on free list        */
  2291.         container->logicalEOF = logicalEOF0 + slopSize;                    /* back up logicalEOF                */
  2292.     } /* for (;two possible tries;) */                                                /* try, try, again...                */
  2293. #endif
  2294.     
  2295.     return (true);                                                                                        /* TOC successfully written!*/
  2296. }
  2297.  
  2298.  
  2299. /*-------------------------------------*
  2300.  | cmDoBackPathes - back-patch the TOC |
  2301.  *-------------------------------------*
  2302.  
  2303.  When a TOC is written (e.g., cmWriteTOC()), there are some entries that are dependent on
  2304.  the final size of the TOC.  Such entries must be back-pached with the proper info.  Only
  2305.  the callers generating the TOC in the container know when it is time to do the patching.
  2306.  They call this routine to do it when that time comes.
  2307.  
  2308.  True is returned if the back-patching is successful and false otherwise.
  2309.  
  2310.  By the time this routine is called, the original TOC entries in question and their
  2311.  offsets within the container have been recorded in a BackPatches struct whose pointer is
  2312.  passed in thePatches.  Currently only 3 such entries need back-patching:
  2313.  
  2314.  tocSizeEntry                      -    The TOC size entry.  This is a property of TOC #1 which
  2315.                                                      represents the size and offset of the TOC itself.  The caller
  2316.                                                     passes this info in the tocSize and tocStart parameters
  2317.                                                     respectively.
  2318.                                          
  2319.  tocContEntry                      -    The TOC container entry. This is a property of TOC #1 which
  2320.                                                      represents the entire container, from offset 0 to the end of the
  2321.                                                     label.  No additional parameters are passed for this.  But to
  2322.                                                     compute this, it is required that cmDoBackPathes() be the LAST
  2323.                                                     thing called prior to writing the container label.
  2324.     
  2325.  tocNewValuesTOCEntry -  The non-private TOC offset/size entry.  An updating TOC contains
  2326.                                                  a TOC #1 property that defines the offset and size of all TOC
  2327.                                                  entries that are to be applied to this container's target.  The
  2328.                                                  caller passes this info in the newValuesTOCSize and
  2329.                                                  newValuesTOCStart parameters respectively.  For non-updating
  2330.                                                  containers, these are ignored.
  2331. */
  2332.  
  2333. CMBoolean cmDoBackPatches(ContainerPtr container, BackPatchesPtr thePatches,
  2334.                                               CM_ULONG tocStart, CM_ULONG tocSize,
  2335.                                                 CM_ULONG newValuesTOCStart, CM_ULONG newValuesTOCSize)
  2336. {
  2337.     CM_ULONG         size, offset;
  2338.     CM_UCHAR         valueBuffer[8];
  2339.  
  2340.     /* Back-patch the TOC size entry, i.e., the entry that defines the offset and size of */
  2341.     /* the TOC itself...                                                                                                                                    */
  2342.     
  2343.     if ((thePatches->whatWeGot & Patch_TOCsize) == 0) {                /* must have gotten offset!    */
  2344.         Container_Disable(container);
  2345.         ERROR1(CM_err_Internal2, CONTAINERNAME);
  2346.         return (false);
  2347.     }
  2348.     
  2349.     CMformatData(container, &valueBuffer[0], 4, &tocStart);
  2350.     CMformatData(container, &valueBuffer[4], 4, &tocSize);
  2351.     CMfseek(container, thePatches->tocSizeEntry, kCMSeekSet);
  2352.     if (CMfwrite(container, valueBuffer, sizeof(CM_UCHAR), 8) != 8) {
  2353.         Container_Disable(container);
  2354.         ERROR1(CM_err_BadTOCWrite, CONTAINERNAME);
  2355.         return (false);
  2356.     }
  2357.     
  2358.     /* Back-patch the TOC updates TOC entry, i.e., the entry that defines the offset and    */
  2359.     /* size of that portion of an updating container's TOC to be applied as updates to its*/
  2360.     /* target.  Note, this entry only exists in updating containers and we only get into    */
  2361.     /* this function it it's a new updating container.                                                                        */
  2362.     
  2363.     if (UPDATING(container)) {                                                                    /* if expecting patch...    */
  2364.         if ((thePatches->whatWeGot & Patch_TOCNewValuesTOC) == 0){/*must have gotten offset!*/
  2365.             Container_Disable(container);
  2366.             ERROR1(CM_err_Internal5, CONTAINERNAME);
  2367.             return (false);
  2368.         }
  2369.         
  2370.         CMformatData(container, &valueBuffer[0], 4, &newValuesTOCStart);
  2371.         CMformatData(container, &valueBuffer[4], 4, &newValuesTOCSize);
  2372.         CMfseek(container, thePatches->tocNewValuesTOCEntry, kCMSeekSet);
  2373.         if (CMfwrite(container, valueBuffer, sizeof(CM_UCHAR), 8) != 8) {
  2374.             Container_Disable(container);
  2375.             ERROR1(CM_err_BadTOCWrite, CONTAINERNAME);
  2376.             return (false);
  2377.         }
  2378.     }
  2379.     
  2380.     /*               ********** The following must be done last! **********               */
  2381.     
  2382.     /* Back-patch the TOC container entry, i.e., the entry that defines the size of the        */
  2383.     /* entire container, including its label.  This entry must be done last and this            */
  2384.     /* routine must be called just prior to writing the container label.  The label    is         */
  2385.     /* assumed to always immediatly follow the TOC so, even though it is not yet written, */
  2386.     /* we can take it into account in setting the size field.                                                            */
  2387.         
  2388.     if ((thePatches->whatWeGot & Patch_TOCContSize) == 0) {        /* must have gotten offset    */
  2389.         Container_Disable(container);
  2390.         ERROR1(CM_err_Internal3, CONTAINERNAME);
  2391.         return (false);
  2392.     } 
  2393.     
  2394.     offset = 0;
  2395.     size      = LBLsize + ((container->embeddedValue) ? (CM_ULONG)CMgetContainerSize(container)+container->mergeInSize
  2396.                                                                                                  : container->physicalEOF);
  2397.     
  2398.     CMformatData(container, &valueBuffer[0], 4, &offset);
  2399.     CMformatData(container, &valueBuffer[4], 4, &size);
  2400.     CMfseek(container, thePatches->tocContEntry, kCMSeekSet);
  2401.     if (CMfwrite(container, valueBuffer, sizeof(CM_UCHAR), 8) != 8) {
  2402.         Container_Disable(container);
  2403.         ERROR1(CM_err_BadTOCWrite, CONTAINERNAME);
  2404.         return (false);
  2405.     }
  2406.  
  2407.     return (true);                                                                                        /* success!                                    */
  2408. }
  2409.                                                           
  2410.                                                             CM_END_CFUNCTIONS
  2411.